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.IP

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Pass exactly one column name: primary_key=("id",) or primary_key=["id"].
  2. Pick a single unique scalar field as the document id.
  3. 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

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


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