cocoindex-io/cocoindex · error · ValueError
Primary key {schema.primary_key!r} value cannot be None.
Error message
Primary key {schema.primary_key!r} value cannot be None. What it means
Guard in CollectionTarget.declare_row. The primary key value doubles as the zvec document id, which must be a concrete value; a None PK (e.g. an optional-typed field left unset in the declared row) cannot be converted to a document id, so the row would be silently unidentifiable. Raised when the row's PK field is None at declaration time. Ensure every declared row has a non-None primary key value.
Source
Thrown at python/cocoindex/connectors/zvec/_target.py:902
_schema: CollectionSchema[RowT]
def __init__(
self,
provider: coco.TargetStateProvider[_DocValue, None, coco.MaybePendingS],
schema: CollectionSchema[RowT],
) -> None:
self._provider = provider
self._schema = schema
def declare_row(self: "CollectionTarget[RowT]", *, row: RowT) -> None:
"""Declare a document (row) to be upserted to this collection.
The primary-key value becomes the document id (converted to ``str``).
"""
schema = self._schema
pk_value = _row_get(row, schema.primary_key)
if pk_value is None:
raise ValueError(
f"Primary key {schema.primary_key!r} value cannot be None."
)
doc_id = str(pk_value)
vectors: dict[str, Any] = {}
fields: dict[str, Any] = {}
for name, col in schema.columns.items():
if name == schema.primary_key:
continue
value = _row_get(row, name)
if col.kind == "dense":
vectors[name] = None if value is None else _to_float_list(value)
elif col.kind == "sparse":
vectors[name] = (
None
if value is None
else {int(k): float(v) for k, v in dict(value).items()}
)View on GitHub (pinned to e84aa99b32)
Solutions
- Ensure every row has a non-None primary-key value before declaring.
- Generate the id in your @coco.fn before declare_row (e.g. uuid or hash of content).
- If the id can be absent, skip the row or use a sentinel/default value.
Example fix
// before target.declare_row(row=Doc(id=None, vec=embedding)) // after import uuid target.declare_row(row=Doc(id=str(uuid.uuid4()), vec=embedding))
Defensive patterns
Strategy: validation
Validate before calling
pk = getattr(row, schema.primary_key, None)
if pk is None:
raise ValueError(f"row missing primary key {schema.primary_key!r}") Type guard
def has_pk(row, pk_name: str) -> bool:
return getattr(row, pk_name, None) is not None Try / catch
try:
target.declare_row(row=row)
except ValueError as e:
if "cannot be None" in str(e):
logging.warning("skipping row with missing id: %r", row) Prevention
- Assign ids (uuid/hash) before declaring rows.
- Avoid Optional primary-key fields in record types.
- Filter out rows with None ids upstream.
When it happens
Trigger: Calling collection_target handler's declare_row(row=...) where the row's primary-key field is None (unset, or an Optional field defaulting to None).
Common situations: Optional id fields in dataclasses; rows built from upstream data with missing ids; autoincrement ids not yet assigned at declare time.
Related errors
- Primary key column {primary_key!r} must be a scalar field, g
- zvec collections require exactly one primary key column (map
- Primary key column '{pk}' not found in columns: {list(self.c
- primary_key {primary_key!r} not found in columns ({sorted(co
- row is missing primary key field {self._primary_key!r}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/1653947e73f37875.
Report an issue: GitHub.