Troubleshooting

On this page

This page documents error conditions and quirks that were confirmed by reading the source code, including two apparent defects worth knowing about before you hit them in production.

Chroma add() legacy style: NameError {#chroma-add-legacy-style-namerror}

Symptom: calling ChromaVectorDatabase.add() the way its own docstring shows for backward compatibility —

Python
db.add(ids=["doc1", "doc2"], documents=["Hello world", "ML"], metadatas=[{"source": "web"}, {}])

— raises NameError, not a successful upsert.

Why: add()'s actual signature is def add(self, chunks, embeddings=None). There is no ids, documents, or metadatas parameter, so passing them as keyword arguments would itself raise TypeError: add() got an unexpected keyword argument 'ids' — that's the first way this fails. If, hypothetically, a caller worked around that by monkeypatching or the surrounding code happened to have module/enclosing-scope variables literally named documents and metadatas, the "legacy" branch inside add() reads:

Python · 3 lines
ids = chunks
documents = documents      # not a parameter — refers to an outer-scope or undefined name
metadatas = [sanitize_metadata(m) for m in (metadatas or [])]   # same problem

which is a self-referential assignment to an undefined name in the normal case, causing NameError: name 'documents' is not defined (and likewise for metadatas) the moment that branch executes.

Workaround: use the unified calling style instead, passing a list of DocumentChunk objects:

Python · 7 lines
from zerda.chunks import DocumentChunk

chunks = [
    DocumentChunk(chunk_id="doc1", doc_id="doc1", text="Hello world", metadata={"source": "web"}),
    DocumentChunk(chunk_id="doc2", doc_id="doc2", text="ML", metadata={}),
]
db.add(chunks)

This path is fully functional and is what the rest of this documentation recommends throughout.

FAISS: IndexIVFFlat errors before training

Symptom: using index_type='ivf' and calling add()/search() while total vectors are still below DEFAULT_IVF_TRAIN_SIZE = 100 can surface a FAISS-level error about an untrained index.

Why: _should_train_ivf() only triggers training once cumulative vectors reach 100. Below that, self.index.is_trained stays False, and FAISS's own IndexIVFFlat.add()/search() require training first.

Workaround: either use index_type='flat' for small datasets, or add at least 100 vectors in your first batch(es) so training triggers, or add vectors in a batch large enough to satisfy the threshold on the very first call.

Pinecone: new index creation can block for minutes

Symptom: PineconeVectorDatabase(...) against a new index_name appears to "hang" for up to five minutes, or raises TimeoutError.

Why: _wait_for_index_ready() polls once per second until Pinecone reports the newly created index as ready, up to DEFAULT_INDEX_READY_TIMEOUT = 300 seconds.

Workaround: create the index ahead of time (out of the hot path of request handling), or budget for a multi-minute first call when standing up a new index.

Pinecone: distance_metric='ip' is rejected

Symptom: PineconeVectorDatabase(distance_metric='ip') raises ValueError: Unsupported distance metric: 'ip'.

Why: 'ip' exists in METRIC_MAPPING (mapped to 'dotproduct') but is absent from SUPPORTED_METRICS, and _validate_metric() checks against SUPPORTED_METRICS first.

Workaround: use 'dotproduct' or 'dot' instead of 'ip' for this backend specifically. (Note this differs from FAISS and Chroma, both of which do accept 'ip'.)

Pinecone: get_stats() can silently change shape

Symptom: code that reads stats['total_vector_count'] sometimes gets a KeyError.

Why: PineconeVectorDatabase.get_stats() catches every exception internally and returns {'error': str(e)} instead of propagating — unlike FAISS/Chroma's get_stats(), which don't have a try/except around their return.

Workaround: check for an 'error' key before assuming the full stats shape is present:

Python · 5 lines
stats = db.get_stats()
if "error" in stats:
    ...  # handle the failure
else:
    print(stats["total_vector_count"])

"No embedder available" ValueError

Symptom: calling add()/search()/add_chunk() with a text query or chunk that has no pre-computed embedding, on a database constructed with embedder=None.

Why: every backend requires either an embedder at construction time or fully pre-computed embeddings passed explicitly to skip embedding generation.

Workaround: either pass an embedder to the constructor, or always supply pre-computed embeddings=... to add()/add_chunk() and only ever query with vectors (np.ndarray), never raw text, when embedder is None.

Dimension mismatch ValueError

Symptom: ValueError: Embedding dimension mismatch. Expected X, got Y.

Why: all three backends fix embedding_dim at construction/first-use time and validate every subsequent add()/search() call's embeddings against it.

Workaround: ensure a single embedder/model (and thus a single output dimension) is used consistently for a given database instance; don't mix embedding models across calls.

Related pages