cocoindex-io/cocoindex · error · TypeError

record_type must be a record type (dataclass, NamedTuple, Py

Error message

record_type must be a record type (dataclass, NamedTuple, Pydantic model), got {type(record_type)}

What it means

from_class() maps a Python record type (dataclass, NamedTuple, or Pydantic model) to zvec collection columns. If the passed object is not one of these recognized record types (checked via is_record_type), a TypeError is raised because column inference cannot proceed.

Source

Thrown at python/cocoindex/connectors/zvec/_target.py:496

        record_type: type[RowT],
        primary_key: list[str],
        *,
        column_overrides: dict[
            str,
            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:

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Decorate the class with @dataclasses.dataclass, or use typing.NamedTuple, or use a Pydantic BaseModel.
  2. Pass the class itself, not an instance.
  3. Check that is_record_type(record_type) returns True before calling from_class.

Example fix

// before
schema = RecordTarget.from_class({"id": str, "vec": list[float]}, primary_key=("id",))
// after
@dataclass
class Doc:
    id: str
    vec: list[float]
schema = RecordTarget.from_class(Doc, primary_key=("id",))
Defensive patterns

Strategy: type-guard

Validate before calling

import dataclasses
if not (dataclasses.is_dataclass(record_type) or issubclass(record_type, tuple) or issubclass(record_type, BaseModel)):
    raise TypeError("need dataclass/NamedTuple/Pydantic model")

Type guard

def is_record_type(t: Any) -> bool:
    import dataclasses
    return dataclasses.is_dataclass(t) or (isinstance(t, type) and issubclass(t, tuple)) or (isinstance(t, type) and issubclass(t, BaseModel))

Try / catch

try:
    target = RecordTarget.from_class(record_type, primary_key=("id",))
except TypeError as e:
    logging.error("unsupported record type: %s", e)

Prevention

When it happens

Trigger: Calling RecordTarget.from_class() with a plain dict, TypedDict, plain class, tuple, or an unannotated class instead of a dataclass/NamedTuple/Pydantic model.

Common situations: Using a TypedDict (not supported as a record type here); passing a Pydantic v1 model when only v2 is detected; passing the class instance instead of the class; forgetting @dataclass decorator.

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/19ae81725b2fe54d. Report an issue: GitHub.