cocoindex-io/cocoindex · error · ValueError
row is missing primary key field {self._primary_key!r}
Error message
row is missing primary key field {self._primary_key!r} What it means
Neo4j's TableTarget.declare_record() converts the row to a dict and requires the configured primary key field to be present, since the primary key value identifies the node to upsert. If the row dict lacks that field, no node key can be computed and the library raises ValueError instead of writing an incomplete record.
Source
Thrown at python/cocoindex/connectors/neo4j/_target.py:1247
) -> None:
self._provider = provider
self._table_schema = table_schema
self._table_name = table_name
self._primary_key = primary_key
@property
def table_name(self) -> str:
return self._table_name
@property
def primary_key(self) -> str:
return self._primary_key
def declare_record(self: TableTarget[RowT], *, row: RowT) -> None:
"""Declare a record (node) to be upserted to this table."""
row_dict = self._row_to_dict(row)
if self._primary_key not in row_dict:
raise ValueError(f"row is missing primary key field {self._primary_key!r}")
pk_values = (row_dict[self._primary_key],)
coco.declare_target_state(self._provider.target_state(pk_values, row_dict))
declare_row = declare_record
def _row_to_dict(self, row: RowT) -> dict[str, Any]:
if self._table_schema is not None:
out: dict[str, Any] = {}
for col_name, col in self._table_schema.columns.items():
if isinstance(row, dict):
value = row.get(col_name)
else:
value = getattr(row, col_name)
if value is not None and col.encoder is not None:
value = col.encoder(value)
out[col_name] = value
return out
if isinstance(row, dict):View on GitHub (pinned to e84aa99b32)
Solutions
- Add the primary key field to the row object/dict you pass to declare_record.
- Declare the table with primary_key=<your actual field name> so it matches the row's key.
- Check what _row_to_dict produces (e.g. dataclass asdict) and confirm the field name spelling/case matches primary_key exactly.
Example fix
// before
target.declare_row(row={"node_id": 7, "name": "Alice"})
// after
target.declare_row(row={"id": 7, "name": "Alice"}) # or declare table with primary_key="node_id" Defensive patterns
Strategy: validation
Validate before calling
pk = target._primary_key if hasattr(target, "_primary_key") else "id"
row_dict = row if isinstance(row, dict) else dataclasses.asdict(row)
if pk not in row_dict:
raise ValueError(f"row missing primary key field {pk!r}")
target.declare_record(row=row) Type guard
def has_pk(row: dict, pk: str) -> bool:
return isinstance(row, dict) and pk in row Prevention
- Keep the row's PK field name and the table's primary_key argument defined from one shared constant.
- For dataclasses, ensure the PK is a declared field so _row_to_dict includes it.
- Prefer schema-driven tables (pass table_schema) so the PK has a single source of truth.
When it happens
Trigger: Calling table_target.declare_record(row=...) (or declare_row) with a row object/dict whose serialized form does not contain a key equal to the table's primary_key (default "id").
Common situations: Using a dataclass or dict with a differently named key (e.g. "key" or "node_id") while the table was declared with primary_key="id"; forgetting to include the PK in dicts built dynamically; renaming the schema field without updating primary_key.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- primary_key {primary_key!r} not found in columns ({sorted(co
- PK column '{pk}' not in columns: {list(self.columns.keys())}
- build_node_upsert requires at least one primary key field
- build_node_delete requires at least one primary key field
- build_relationship_upsert requires PK fields for from, to, a
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/e623098a05bccf26.
Report an issue: GitHub.