RyanCodrai/turbovec · error · ValueError
expected 2D embedding batch, got {vectors.ndim}D
Error message
expected 2D embedding batch, got {vectors.ndim}D What it means
add() converts node embeddings to a numpy float32 batch and requires a 2D (N, dim) array. ndim != 2 means the embeddings are ragged, scalar, or empty, so the batch cannot be interpreted as a matrix of vectors.
Source
Thrown at turbovec-python/python/turbovec/llama_index.py:363
# back to one of them — the earlier handles become orphans that
# `query` later resolves through the duplicate node_id, returning
# the second node's payload attached to the first node's vector.
# Caller's job to deduplicate before calling add.
node_ids = [n.node_id for n in nodes]
try:
resolve_duplicates(node_ids, DuplicatePolicy.REJECT)
except ValueError:
seen: set[str] = set()
dup = next(nid for nid in node_ids if nid in seen or seen.add(nid))
raise ValueError(
f"duplicate node_id {dup!r} appears multiple times "
"in the input batch; deduplicate before calling add()"
) from None
embeddings = [node.get_embedding() for node in nodes]
vectors = np.asarray(embeddings, dtype=np.float32)
if vectors.ndim != 2:
raise ValueError(
f"expected 2D embedding batch, got {vectors.ndim}D"
)
# A batch of empty per-node embeddings has shape (N, 0) — 2D, so
# it passes the ndim guard, then dies deep in the index kernel
# with an opaque buffer-length error. Name the real cause instead.
if vectors.shape[1] == 0:
raise ValueError(
"nodes have empty embeddings (dim 0); check the embed "
"model that produced them"
)
# Build every side-car payload BEFORE mutating any state, so a
# payload failure (e.g. non-serializable metadata) leaves the
# store untouched. `metadata` and `ref_doc_id` are kept at top
# level for fast filter / doc-id lookup (queries hit these on
# every hit; parsing _node_content per hit would be wasteful).
# `node_dict` is the framework's canonical metadata representation
# (`_node_content` + `_node_type` + original metadata keys),
# which `metadata_dict_to_node` reconstructs into a fullView on GitHub (pinned to ccab9f325e)
Solutions
- Ensure all nodes were embedded by the same model so every embedding has the same length
- Embed nodes before adding: call embed_model.get_text_embedding_batch([n.get_content() for n in nodes]) and set each node's embedding
- Check vectors.shape before calling add()
Example fix
// before
store.add(nodes) # nodes not embedded
// after
embeddings = embed_model.get_text_embedding_batch([n.get_content() for n in nodes])
for n, e in zip(nodes, embeddings):
n.embedding = e
store.add(nodes) Defensive patterns
Strategy: validation
Validate before calling
import numpy as np
vecs = np.asarray([n.get_embedding() for n in nodes], dtype=np.float32)
assert vecs.ndim == 2, f"embedding batch is {vecs.ndim}D" Type guard
def embeddings_are_2d(nodes) -> bool:
import numpy as np
return np.asarray([n.get_embedding() for n in nodes]).ndim == 2 Try / catch
try:
store.add(nodes)
except ValueError as e:
if "expected 2D embedding batch" in str(e):
raise RuntimeError("nodes are inconsistently embedded; re-run embed model") from e
raise Prevention
- Embed all nodes with one model before add()
- Validate embedding lengths are uniform in pipeline tests
- Never mix embedding sources in a single batch
When it happens
Trigger: Calling add() with nodes whose get_embedding() returns lists of unequal length (ragged), a single scalar, or an empty list across all nodes causing a non-2D array from np.asarray.
Common situations: Mixing embeddings from different embed models with different dimensions; nodes never embedded (None/empty embeddings); passing a single node's 1D embedding wrapped incorrectly.
Related errors
- nodes have empty embeddings (dim 0); check the embed model t
- node embedding dim {vectors.shape[1]} does not match index d
- module {__name__!r} has no attribute {name!r}
- duplicate id in batch: {k!r}
- {prefix} {version}; this turbovec accepts versions {list(com
AI-assisted analysis of RyanCodrai/turbovec@ccab9f325e (2026-09-06).
Data as JSON: /api/errors/ad0d77191a78bc6a.
Report an issue: GitHub.