cocoindex-io/cocoindex · error · ValueError
Row {row.id!r}: attribute name {k!r} is reserved.
Error message
Row {row.id!r}: attribute name {k!r} is reserved. What it means
In turbopuffer's wire format, the row id and all vector field names live at the top level of the upsert payload, so a user attribute with one of those names would collide. `_row_to_upsert` builds `reserved = {"id"} | vector_field_names` and raises this ValueError if any key in `row.attributes` matches, preventing silent data corruption on write.
Source
Thrown at python/cocoindex/connectors/turbopuffer/_target.py:228
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."""
dt = np.dtype(vs.dtype)
if dt == np.float32:
suffix = "f32"
elif dt == np.float16:
suffix = "f16"
else:
raise ValueError(
f"Turbopuffer vectors only support float32 or float16, got {dt}."
)
return f"[{vs.size}]{suffix}"
View on GitHub (pinned to e84aa99b32)
Solutions
- Rename the attribute key to something not in {"id"} ∪ declared vector field names, e.g. "doc_id" or "source_id".
- Filter or remap reserved keys before constructing Row: drop or prefix them in your mapping function.
- If the attribute genuinely duplicates the row id, just remove it — the id is already written at the top level.
Example fix
// before
Row(id=doc["id"], vector=vec, attributes=doc) # doc contains 'id' and 'title_vec'
// after
attrs = {k: v for k, v in doc.items() if k not in {"id", "title_vec"}}
Row(id=doc["id"], vector=vec, attributes=attrs) Defensive patterns
Strategy: validation
Validate before calling
reserved = {"id"} | set(schema.vectors.vectors)
bad = set(row.attributes or {}) & reserved
assert not bad, f"Reserved attribute keys: {bad}" Try / catch
try:
await component.reconcile(row)
except ValueError as e:
m = re.search(r"attribute name (.+?) is reserved", str(e))
if m:
row = rename_attribute(row, ast.literal_eval(m.group(1)))
else:
raise Prevention
- Sanitize external metadata (CSV/JSON) by renaming or dropping 'id' and vector-field-named keys before building Rows
- Prefix attribute keys with a namespace like 'meta_' when ingesting arbitrary records
- Keep the reserved-name list next to the schema definition and share it with the sanitizer
When it happens
Trigger: Row(id=1, vector=..., attributes={"id": "x"}) or attributes containing a key equal to a declared vector field name (e.g. "title_vec") when reconcile serializes the row.
Common situations: Storing source document metadata that includes an 'id' or 'vector'-like field; bulk-uploading records whose field names came from a CSV/JSON that already contains 'id'; accidentally dumping whole objects into attributes instead of picking non-reserved keys.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- turbopuffer is required to use the Turbopuffer connector. Pl
- Invalid vector definition: {vector_def}
- Named-vectors dict is empty; declare at least one vector fie
- Vector field name {sorted(reserved)[0]!r} is reserved (it co
- Invalid vector definition: {vectors}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/19cde31f8b90edd0.
Report an issue: GitHub.