Skip to content

Bug: PGVector.add_documents/add_embeddings silently truncates documents via zip and falsely reports all IDs as inserted #300

Description

Related to issue:
langchain-ai/langchain-google#1704

Hi, I ran into an issue with langchain_google_genai's embedding results. It silently returns 1 embedding when using gemini-embedding-2. The silent failure is further hidden by langchain-postgres PGVector.add_embeddings.

PGVector.add_embeddings builds its insert payload using zip(texts, metadatas, embeddings, ids_), which silently truncates to the shortest list. It then returns ids_ (the full input list) regardless of how many rows were actually written, so the caller sees N IDs returned with no exception, but only 1 row in the database.

Reproduce

Here's a script to reproduce the error:

from langchain_core.documents import Document
from langchain_google_genai import GoogleGenerativeAIEmbeddings
from langchain_postgres import PGVector
import psycopg2
import os
from dotenv import load_dotenv

load_dotenv()

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
DATABASE_URL = os.getenv("DATABASE_URL", "")

embeddings = GoogleGenerativeAIEmbeddings(
    model="gemini-embedding-2",
    api_key=GOOGLE_API_KEY,
)

docs = [
    Document(
        page_content="there are cats in the pond",
        metadata={"id": 1, "location": "pond", "topic": "animals"},
    ),
    Document(
        page_content="ducks are also found in the pond",
        metadata={"id": 2, "location": "pond", "topic": "animals"},
    ),
    Document(
        page_content="fresh apples are available at the market",
        metadata={"id": 3, "location": "market", "topic": "food"},
    ),
]

result = embeddings.embed_documents([d.page_content for d in docs])
print(
    f"[BUG 1 - langchain-google-genai] embed_documents: passed {len(docs)} texts, got {len(result)} vectors"
)
# Expected: 3, Actual: 1


vector_store = PGVector(
    embeddings=embeddings,
    collection_name="rag_docs",
    connection=DATABASE_URL,
)

inserted_ids = vector_store.add_documents(docs)
print(
    f"[BUG 2 - langchain-postgres] add_documents: passed {len(docs)} docs, returned {len(inserted_ids)} IDs (false report)"
)
# Expected: 3 docs, 1 inserted (since embeddings only return 1 result)
# Actual: 3 docs, 3 inserted (hiding that only one embedding was processed)


conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM langchain_pg_embedding")
count = cur.fetchone()[0]
print(f"[BUG 2 - langchain-postgres] actual rows in DB: {count}")
# Actual: 1 row in DB

Root Cause

# langchain_postgres/vectorstores.py

def add_embeddings(
    self,
    texts: Sequence[str],
    embeddings: List[List[float]],
    metadatas: Optional[List[dict]] = None,
    ids: Optional[List[str]] = None,
    **kwargs: Any,
) -> List[str]:
    ...
    data = [
        {
            "id": id,
            "collection_id": collection.uuid,
            "embedding": embedding,
            "document": text,
            "cmetadata": metadata or {},
        }
        for text, metadata, embedding, id in zip(
            texts, metadatas, embeddings, ids_
        ) # embeddings truncates the loop
    ]
    ...

    return ids_ # returns full list

Suggested Fix

Before inserting the data, we can validate that the number of embeddings match the number of text inputs

 if len(embeddings) != len(texts):
      raise ValueError(
          f"Embedding count mismatch: expected {len(texts)}, got {len(embeddings)}. No documents inserted."
      )

Library Versions

langchain==1.2.15
langchain-classic==1.0.4
langchain-community==0.4.1
langchain-core==1.3.2
langchain-google-genai==4.2.2
langchain-postgres==0.0.17
langchain-protocol==0.0.12
langchain-text-splitters==1.1.2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions