cocoindex-io/cocoindex · error · ValueError
zvec collections require exactly one primary key column (map
Error message
zvec collections require exactly one primary key column (mapped to the document id), got {primary_key}. What it means
zvec collections map the primary-key column value to the document id, and zvec requires exactly one such column. from_class() raises ValueError when primary_key is not a sequence of exactly one column name.
Source
Thrown at python/cocoindex/connectors/zvec/_target.py:501
ZvecType | ZvecVectorDef | ZvecFtsType | res_schema.VectorSchemaProvider,
]
| None = None,
) -> "CollectionSchema[RowT]":
"""Build a CollectionSchema from a record type.
Args:
record_type: A dataclass, NamedTuple, or Pydantic model.
primary_key: Exactly one column name. Its value becomes the document
id (converted to ``str``).
column_overrides: Optional per-column type/vector overrides.
"""
if not is_record_type(record_type):
raise TypeError(
"record_type must be a record type (dataclass, NamedTuple, "
f"Pydantic model), got {type(record_type)}"
)
if len(primary_key) != 1:
raise ValueError(
"zvec collections require exactly one primary key column "
f"(mapped to the document id), got {primary_key}."
)
record_info = RecordType(record_type)
columns: dict[str, _Column] = {}
for fld in record_info.fields:
override = column_overrides.get(fld.name) if column_overrides else None
columns[fld.name] = await _resolve_column(fld.name, fld.type_hint, override)
return cls(columns, primary_key[0], row_type=record_type)
def _metric_type(metric: str) -> Any:
key = metric.lower()
if key == "cosine":
return _zvec.MetricType.COSINE
if key == "ip":
return _zvec.MetricType.IPView on GitHub (pinned to e84aa99b32)
Solutions
- Pass exactly one column name: primary_key=("id",) or primary_key=["id"].
- Pick a single unique scalar field as the document id.
- If you need a composite id, combine values into one scalar field (e.g. a formatted string) and use that.
Example fix
// before
RecordTarget.from_class(Doc, primary_key=("tenant_id", "doc_id"))
// after
RecordTarget.from_class(Doc, primary_key=("doc_id",)) Defensive patterns
Strategy: validation
Validate before calling
if len(primary_key) != 1:
raise ValueError("zvec requires exactly one primary key column") Try / catch
try:
target = RecordTarget.from_class(Doc, primary_key=primary_key)
except ValueError as e:
logging.error("bad primary_key: %s", e) Prevention
- Use a single-element tuple like ("id",) for primary_key.
- Combine composite keys into one scalar string field.
- Remember zvec document ids are single-valued.
When it happens
Trigger: Calling from_class(record_type, primary_key=()) with zero names, or primary_key=("id", "other") with multiple names.
Common situations: Copying config from a connector that supports composite keys (e.g. postgres multi-column keys); passing a string with a comma or a list of candidate key columns.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Primary key column {primary_key!r} must be a scalar field, g
- Primary key column '{pk}' not found in columns: {list(self.c
- Primary key {schema.primary_key!r} value cannot be None.
- zvec collections require at least one vector field (dense or
- Primary key column '{pk}' not found in columns: {list(self.c
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/a5d7304478a7d3bf.
Report an issue: GitHub.