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

The Snowflake target's `from_class` requires `record_type` to be a structured record type (dataclass, NamedTuple, or Pydantic model) from which column definitions can be derived. Passing any other type (dict, plain class, None, etc.) fails the `is_record_type` check and raises a TypeError. This enforces that table columns can be introspected from field annotations.

Source

Thrown at python/cocoindex/connectors/snowflake/_target.py:141

    @classmethod
    async def from_class(
        cls,
        record_type: type[RowT],
        primary_key: list[str],
        *,
        column_overrides: dict[str, SnowflakeType] | None = None,
    ) -> "TableSchema[RowT]":
        """
        Create a TableSchema from a record type.

        Args:
            record_type: A dataclass, NamedTuple, or Pydantic model.
            primary_key: List of column names that form the primary key.
            column_overrides: Optional per-column SnowflakeType overrides.
        """
        if not is_record_type(record_type):
            raise TypeError(
                f"record_type must be a record type (dataclass, NamedTuple, Pydantic model), "
                f"got {type(record_type)}"
            )
        columns = await cls._columns_from_record_type(record_type, column_overrides)
        return cls(columns, primary_key, row_type=record_type)

    @staticmethod
    async def _columns_from_record_type(
        record_type: type,
        column_overrides: dict[str, SnowflakeType] | None,
    ) -> dict[str, ColumnDef]:
        """Convert a record type to a dict of column name -> ColumnDef."""
        record_info = RecordType(record_type)
        columns: dict[str, ColumnDef] = {}

        for field in record_info.fields:
            override = column_overrides.get(field.name) if column_overrides else None
            type_info = analyze_type_info(field.type_hint)

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Decorate the class with @dataclasses.dataclass (with type annotations on all fields) and pass that class.
  2. Use a typing.NamedTuple subclass with annotated fields instead.
  3. If using Pydantic, ensure it is a supported (BaseModel) class and that pydantic is installed.
  4. Check for typos — e.g. passing an instance or the module instead of the class.

Example fix

// before
spec = {"id": int, "text": str}
target = snowflake.table_target(record_type=spec, ...)
// after
@dataclasses.dataclass
class RowSpec:
    id: int
    text: str

target = snowflake.table_target(record_type=RowSpec, ...)
Defensive patterns

Strategy: type-guard

Validate before calling

import dataclasses, typing
from pydantic import BaseModel

def is_valid_record_type(rt) -> bool:
    return dataclasses.is_dataclass(rt) or (
        isinstance(rt, type) and issubclass(rt, tuple) and hasattr(rt, "_fields")
    ) or (isinstance(rt, type) and issubclass(rt, BaseModel))

assert is_valid_record_type(RowSpec), "record_type must be a dataclass/NamedTuple/Pydantic model"

Type guard

def is_record_type(rt: object) -> TypeGuard[type]:
    import dataclasses
    from pydantic import BaseModel
    return (
        isinstance(rt, type)
        and (dataclasses.is_dataclass(rt) or issubclass(rt, BaseModel)
             or (issubclass(rt, tuple) and hasattr(rt, '_fields')))
    )

Try / catch

try:
    target = snowflake.table_target(record_type=RowSpec, ...)
except TypeError as e:
    if "record_type must be a record type" in str(e):
        raise ConfigError("Define RowSpec as @dataclass / NamedTuple / BaseModel") from e
    raise

Prevention

When it happens

Trigger: Calling `SnowflakeTableTarget.from_class(record_type=...)` (directly or via `table_target`) with a dict, a non-annotated plain class, a TypedDict, None, or any object not recognized as a dataclass/NamedTuple/Pydantic model.

Common situations: Passing a TypedDict (not supported), passing a dict describing columns manually, forgetting the @dataclass decorator, or using a Pydantic v1 model where only v2-style models are detected.

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/59bc3da2e3579556. Report an issue: GitHub.