cocoindex-io/cocoindex · error · ValueError

vec0 virtual tables require exactly one primary key column,

Error message

vec0 virtual tables require exactly one primary key column, got {len(table_schema.primary_key)}: {table_schema.primary_key}

What it means

sqlite-vec vec0 virtual tables require exactly one primary key column. The provided table schema declares zero or multiple primary key columns, so CocoIndex rejects the configuration before creating the table.

Source

Thrown at python/cocoindex/connectors/sqlite/_target.py:1147

            "sqlite-vec extension must be loaded for vec0 virtual tables. "
            "Use connect(..., load_vec=True)"
        )
    has_vector_col = any(
        col_def.type.startswith("float[") for col_def in table_schema.columns.values()
    )
    if not has_vector_col:
        raise ValueError(
            "vec0 virtual tables require at least one float[N] vector column"
        )
    all_cols = set(table_schema.columns.keys())
    invalid_partition = set(virtual_table_def.partition_key_columns) - all_cols
    if invalid_partition:
        raise ValueError(f"Partition key columns not in schema: {invalid_partition}")
    invalid_aux = set(virtual_table_def.auxiliary_columns) - all_cols
    if invalid_aux:
        raise ValueError(f"Auxiliary columns not in schema: {invalid_aux}")
    if len(table_schema.primary_key) != 1:
        raise ValueError(
            f"vec0 virtual tables require exactly one primary key column, "
            f"got {len(table_schema.primary_key)}: {table_schema.primary_key}"
        )
    pk_col_name = table_schema.primary_key[0]
    pk_col_type = table_schema.columns[pk_col_name].type
    if pk_col_type != "INTEGER":
        raise ValueError(
            f"vec0 virtual tables require INTEGER primary key, "
            f"got {pk_col_type} for column '{pk_col_name}'"
        )


def table_target(
    db: ContextKey[ManagedConnection],
    table_name: str,
    table_schema: TableSchema[RowT],
    *,
    managed_by: target.ManagedBy = target.ManagedBy.SYSTEM,

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Change the schema so exactly one column is the primary key.
  2. Move extra key columns to regular columns or partition key columns instead.
  3. If no PK was intended, explicitly mark one existing column (e.g. an INTEGER id) as primary key.

Example fix

// before
schema = table_schema(primary_key=["id", "doc_id"])
// after
schema = table_schema(primary_key=["id"], columns=["id", "doc_id", "embedding"])
Defensive patterns

Strategy: validation

Validate before calling

if len(schema.primary_key) != 1:
    raise ValueError("vec0 tables need exactly one primary key column")

Type guard

def has_single_pk(schema) -> bool:
    return len(schema.primary_key) == 1

Try / catch

try:
    target = table_target(schema, virtual_table_def)
except ValueError as e:
    if "exactly one primary key" in str(e):
        schema = replace_primary_key(schema, ["id"])
        ...

Prevention

When it happens

Trigger: Calling table_target() for a vec0 table where table_schema.primary_key has length != 1 (composite PK like ["id", "ts"], or no PK at all).

Common situations: Defining a composite primary key as in normal SQLite/Postgres tables, then reusing the schema for a vec0 table; forgetting to mark any column as primary key when building the schema manually.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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