cocoindex-io/cocoindex · error · ValueError
SQLite primary key column {pk!r} cannot be None
Error message
SQLite primary key column {pk!r} cannot be None What it means
Primary key values must be non-NULL because they form the target-state identity of a row. `declare_row` raises `ValueError` when any primary key column value is `None`, including non-dict (dataclass/NamedTuple/Pydantic) rows whose pk field was left unset.
Source
Thrown at python/cocoindex/connectors/sqlite/_target.py:1094
def declare_row(self: "TableTarget[RowT]", *, row: RowT) -> None:
"""
Declare a row to be upserted to this table.
Args:
row: A row object (dict, dataclass, NamedTuple, or Pydantic model).
Must include all primary key columns with non-None values.
Dict rows may omit nullable non-primary-key columns; omitted values
are written as NULL.
"""
row_dict = self._row_to_dict(row)
pk_values: list[Any] = []
for pk in self._table_schema.primary_key:
if isinstance(row, dict) and pk not in row:
raise ValueError(f"SQLite row is missing primary key column {pk!r}")
pk_value = row_dict[pk]
if pk_value is None:
raise ValueError(f"SQLite primary key column {pk!r} cannot be None")
pk_values.append(pk_value)
coco.declare_target_state(
self._provider.target_state(tuple(pk_values), row_dict)
)
def _row_to_dict(self, row: RowT) -> dict[str, Any]:
"""
Convert a row (dict or object) into dict[str, Any] using the schema columns,
and apply column encoders for both dict and object inputs.
"""
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:View on GitHub (pinned to e84aa99b32)
Solutions
- Provide a concrete non-None value for every primary key column before declaring the row
- Make the pk field required in the record type so it cannot default to None
- Generate ids yourself (uuid/counter) before calling `declare_row`
Example fix
// before
@dataclass
class Row:
id: int | None = None
target.declare_row(Row())
// after
@dataclass
class Row:
id: int
target.declare_row(Row(id=uuid4().int)) Defensive patterns
Strategy: validation
Validate before calling
for pk in primary_keys:
if row[pk] is None:
raise ValueError(f"{pk} must be non-None before declare_row") Type guard
def pks_present(row: dict, pks: list[str]) -> bool:
return all(row.get(pk) is not None for pk in pks) Try / catch
try:
target.declare_row(row)
except ValueError as e:
if "cannot be None" in e.args[0]:
row[pk_field] = generate_id()
else:
raise Prevention
- Declare pk fields as required (non-Optional) in record types
- Generate ids (uuid/nanoid) before declaring rows — there is no auto-increment in this model
- Reject None pks at the boundary where rows are produced
When it happens
Trigger: Declaring a row where a primary-key field is `None` — e.g. an optional dataclass field defaulting to `None`, or a dict with `"id": None`; also reached for non-dict rows since the check applies to `row_dict[pk]` regardless of row kind.
Common situations: Optional pk fields on dataclasses (`id: int | None = None`) left unset; records built from upstream data where the id failed to populate; auto-increment mental model (SQLite rowid) that does not apply because the engine needs the key up front.
Related errors
- SQLite row is missing primary key column {pk!r}
- PK column '{pk}' not in columns: {list(self.columns.keys())}
- primary_key {primary_key!r} not found in columns ({sorted(co
- row is missing primary key field {self._primary_key!r}
- Invalid vector dimension: {vector_schema.size}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/566a76f2f3f7b41f.
Report an issue: GitHub.