cocoindex-io/cocoindex · error

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

`TableTarget.declare_record` converts the row to a dict and requires the schema's primary key field to be present so it can compute the target-state key. A row lacking that field (after `_row_to_dict`) cannot be keyed/upserted, so ValueError is raised naming the missing field.

Source

Thrown at python/cocoindex/connectors/falkordb/_target.py:1273

    ) -> 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. Ensure every row passed to `declare_record` includes the primary key field with a non-None value.
  2. Align the row class/annotation with the schema's `row_type` so `_row_to_dict` yields the key.
  3. If the key is derived, compute it explicitly before declaring, e.g. `row = Person(id=slug(name), ...)`.

Example fix

// before
target.declare_record(row=Person(name="Alice"))  # schema primary_key="id"
// after
target.declare_record(row=Person(id="alice", name="Alice"))
Defensive patterns

Strategy: validation

Validate before calling

row_dict = dataclasses.asdict(row)
assert schema.primary_key in row_dict and row_dict[schema.primary_key] is not None, \
    f"row missing primary key {schema.primary_key!r}"
target.declare_record(row=row)

Type guard

def has_pk(row: object, pk: str) -> bool:
    return getattr(row, pk, None) is not None

Try / catch

try:
    target.declare_record(row=row)
except ValueError as e:
    if "missing primary key field" in str(e):
        logging.error("Row %r lacks key field; regenerate rows with id set: %s", row, e)
    raise

Prevention

When it happens

Trigger: Calling `target.declare_record(row=...)` (or `declare_row`) with a row object whose dict form lacks the primary key field — e.g. a dataclass missing the `id` field, or a plain object whose attribute name differs from the schema key.

Common situations: Row type drifted from the schema (field renamed); constructing rows ad-hoc in a graph-building function (see callers like `build_graph`, `sync_person_graph`) and forgetting the `id`; a custom row object not matching the declared `row_type`.

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/228b1b716fe8b145. Report an issue: GitHub.