RyanCodrai/turbovec · error · ValueError
node embedding dim {vectors.shape[1]} does not match index d
Error message
node embedding dim {vectors.shape[1]} does not match index dim {existing_dim} What it means
The store's underlying index already has a fixed vector dimension and the incoming node embeddings have a different width. For an eager (already-populated) index, adding mismatched-dim vectors would cause a Rust panic, so the Python layer pre-checks and raises a clean ValueError.
Source
Thrown at turbovec-python/python/turbovec/llama_index.py:401
# PARENT / CHILD), excluded_*_metadata_keys, template fields,
# start/end_char_idx and mimetype on retrieval. The narrow
# `{text, metadata, ref_doc_id}` schema we used to keep lost
# all of those silently.
payloads = [_payload_for(node) for node in nodes]
# Cosine mode: L2-normalize outside the lock (pure computation)
# so the engine's raw inner product is true cosine similarity.
# Zero rows pass through unchanged.
if self._similarity == COSINE:
vectors = l2_normalize_rows(vectors)
with self._write_lock:
# IdMapIndex.add_with_ids handles eager (dim must match) and lazy
# (locks dim on first add) — pre-check the eager case so we
# surface a clean ValueError rather than a Rust panic.
existing_dim = self._index.dim
if existing_dim is not None and vectors.shape[1] != existing_dim:
raise ValueError(
f"node embedding dim {vectors.shape[1]} does not match index dim {existing_dim}"
)
if not vectors.flags["C_CONTIGUOUS"]:
vectors = np.ascontiguousarray(vectors)
handles = np.array([self._issue_handle() for _ in nodes], dtype=np.uint64)
# Capture the previous state of any upserted node_id BEFORE the
# maps are overwritten, so a failed index add can restore it and
# the old vectors can be dropped once the add succeeds.
old = [
(nid, self._node_id_to_u64[nid], self._nodes[nid])
for nid in node_ids
if nid in self._node_id_to_u64
]
# Maps BEFORE the index add: a concurrent query can only learn
# a handle from the index, so an entry that is resolvable butView on GitHub (pinned to ccab9f325e)
Solutions
- Rebuild the index from scratch with the new embed model instead of appending to the old one
- Ensure all documents are embedded with the same model used when the index dim was locked
- Delete the persisted index files and re-ingest the corpus
Example fix
// before store = TurboQuantVectorStore(persist_path="old_index.tqv") # 384-dim store.add(new_1536_dim_nodes) // after store = TurboQuantVectorStore(persist_path=None) # fresh index for new dim store.add(new_1536_dim_nodes)
Defensive patterns
Strategy: validation
Validate before calling
dim = store._index.dim
if dim is not None and len(nodes[0].get_embedding()) != dim:
raise ValueError(f"embedding dim {len(nodes[0].get_embedding())} != index dim {dim}") Type guard
def dims_match(nodes, index_dim: int | None) -> bool:
return index_dim is None or all(len(n.get_embedding()) == index_dim for n in nodes) Try / catch
try:
store.add(nodes)
except ValueError as e:
if "does not match index dim" in str(e):
raise RuntimeError("switched embed models? rebuild the index from scratch") from e
raise Prevention
- Never reuse a persisted index after changing embed models
- Record the embed model name/id alongside the index
- Version index files by embedding dim
When it happens
Trigger: Calling add() with embeddings of dimension D2 when the index was created/first-populated with dimension D1 — typically after switching embed models against an existing persisted index.
Common situations: Changing from one embed model (e.g. 384-dim MiniLM) to another (e.g. 1536-dim OpenAI) while reusing a persisted turbovec index; index dim locked by a previous first insert.
Understand the failure class
Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.
Related errors
- duplicate node_id {dup!r} appears multiple times in the inpu
- expected 2D embedding batch, got {vectors.ndim}D
- nodes have empty embeddings (dim 0); check the embed model t
- TurboQuantVectorStore requires a pre-computed query_embeddin
- module {__name__!r} has no attribute {name!r}
AI-assisted analysis of RyanCodrai/turbovec@ccab9f325e (2026-09-06).
Data as JSON: /api/errors/ea02df7dd7632062.
Report an issue: GitHub.