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

  1. Add the primary key field to the row object/dict you pass to declare_record.
  2. Declare the table with primary_key=<your actual field name> so it matches the row's key.
  3. 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

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


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