cocoindex-io/cocoindex · error · TypeError

row_type must be a record type (dataclass, NamedTuple, or…

Error message

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

What it means

When row_type is given, it must be a supported record type — a dataclass, NamedTuple, or Pydantic model — because the connector introspects its fields to derive columns. Passing any other type raises TypeError.

Solutions

  1. Decorate the class with @dataclass, base it on NamedTuple, or make it a Pydantic BaseModel.
  2. Pass the class itself, not an instance.
  3. If fully custom construction is needed, use row_factory instead of row_type.

Example fix

// before
src = PostgresSource(table="users", row_type=dict)
// after
@dataclass
class User:
    id: int
    name: str
src = PostgresSource(table="users", row_type=User)
Defensive patterns

Strategy: type-guard

Validate before calling

import dataclasses, typing
assert dataclasses.is_dataclass(MyRow) or issubclass(MyRow, tuple) or hasattr(MyRow, "model_fields")

Type guard

def is_record_type(t: object) -> bool:
    import dataclasses
    return (
        isinstance(t, type)
        and (dataclasses.is_dataclass(t)
             or issubclass(t, tuple) and hasattr(t, "_fields")
             or hasattr(t, "model_fields"))
    )

Try / catch

try:
    src = PostgresSource(table="t", row_type=R)
except TypeError as e:
    if "must be a record type" in str(e):
        R = make_dataclass_from(R)

Prevention

When it happens

Trigger: Passing row_type that is a plain class, dict, TypedDict, generic list, or an instance instead of the class itself to PostgresSource.__init__.

Common situations: Passing a TypedDict (not introspectable as a record here), forgetting to apply @dataclass, or accidentally passing an instance (User(...)) rather than the class.

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/499ce58b9fa11e4f. Report an issue: GitHub.

Appendix: source

Thrown at python/cocoindex/connectors/postgres/_source.py:211

    def __init__(
        self,
        pool: asyncpg.Pool,
        *,
        table_name: str,
        columns: Sequence[str] | None = None,
        pg_schema_name: str | None = None,
        row_factory: Callable[[dict[str, Any]], RowT] | None = None,
        row_type: type[RowT] | None = None,
    ) -> None:
        if row_factory is not None and row_type is not None:
            raise ValueError("Cannot specify both row_factory and row_type")

        # Determine columns based on row_type
        resolved_columns: Sequence[str] | None = columns
        if row_type is not None:
            if not is_record_type(row_type):
                raise TypeError(
                    f"row_type must be a record type (dataclass, NamedTuple, or Pydantic model), "
                    f"got {row_type}"
                )
            record_info = RecordType(row_type)
            field_names = [f.name for f in record_info.fields]
            field_set = frozenset(field_names)

            if columns is not None:
                # Validate that all specified columns exist in the record type
                invalid_cols = [c for c in columns if c not in field_set]
                if invalid_cols:
                    raise ValueError(
                        f"Columns {invalid_cols} not found in row_type fields: {field_names}"
                    )
            else:
                # Use record type fields as columns
                resolved_columns = field_names

View on GitHub (pinned to e84aa99b32)