cocoindex-io/cocoindex · error · ValueError

Row {row.id!r}: schema declares a single unnamed vector but

Error message

Row {row.id!r}: schema declares a single unnamed vector but row.vector is a dict.

What it means

The inverse of error 240: when the turbopuffer namespace schema declares a single unnamed vector (a plain VectorDef), `row.vector` must be a single sequence of floats. If a dict is passed (the shape used for named vectors), `_row_to_upsert` raises this ValueError because there are no vector field names to key by.

Source

Thrown at python/cocoindex/connectors/turbopuffer/_target.py:217

    out: dict[str, Any] = {"id": row.id}

    if isinstance(schema.vectors, _ResolvedNamedVectorsDef):
        vector_field_names = set(schema.vectors.vectors)
        if not isinstance(row.vector, dict):
            raise ValueError(
                f"Row {row.id!r}: schema declares named vectors "
                f"({sorted(vector_field_names)}) but row.vector is not a dict."
            )
        missing = vector_field_names - set(row.vector)
        if missing:
            raise ValueError(
                f"Row {row.id!r}: missing vector fields {sorted(missing)}."
            )
        for name, vec in row.vector.items():
            out[name] = _vector_to_list(vec)
    else:
        vector_field_names = {_DEFAULT_VECTOR_FIELD}
        if isinstance(row.vector, dict):
            raise ValueError(
                f"Row {row.id!r}: schema declares a single unnamed vector but "
                f"row.vector is a dict."
            )
        out[_DEFAULT_VECTOR_FIELD] = _vector_to_list(row.vector)

    reserved = {"id"} | vector_field_names
    if row.attributes:
        for k, v in row.attributes.items():
            if k in reserved:
                raise ValueError(f"Row {row.id!r}: attribute name {k!r} is reserved.")
            out[k] = v

    return out


def _vector_type_str(vs: res_schema.VectorSchema) -> str:
    """Render a VectorSchema as turbopuffer's ``[N]fXX`` type string."""

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Pass the vector data directly as a sequence/ndarray: Row(id=..., vector=[0.1, 0.2]) with no dict wrapper.
  2. If you need multiple vectors per row, change the schema to declare named vectors (a dict of VectorDefs) so dicts are accepted.
  3. Extract the single embedding from the dict before constructing the Row.

Example fix

// before
Row(id=7, vector={"embedding": embed(text)})

// after
Row(id=7, vector=embed(text))
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(schema.vectors, _ResolvedNamedVectorsDef) and isinstance(row.vector, dict):
    raise TypeError("Single-vector schema: pass the vector directly, not a dict")

Type guard

def is_plain_vector(v: object) -> TypeGuard[Sequence[float] | np.ndarray]:
    return isinstance(v, (np.ndarray, (list, tuple))) and not isinstance(v, dict)

Try / catch

try:
    await component.reconcile(row)
except ValueError as e:
    if "row.vector is a dict" in str(e):
        (only,) = row.vector.values()
        row = replace(row, vector=only)
    else:
        raise

Prevention

When it happens

Trigger: NamespaceSchema created with vectors=VectorDef(...) (unnamed single vector) but the row is built as Row(id=..., vector={"vec": [0.1, 0.2]}) or with a dict of multiple named embeddings, then passed to reconcile.

Common situations: Copying row-construction code from a named-vectors example into a single-vector namespace; switching a schema from named vectors back to a single vector without simplifying the rows; wrapping the embedding in a dict for 'clarity'.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/bba64629555ae9db. Report an issue: GitHub.