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
- Decorate the class with @dataclass, base it on NamedTuple, or make it a Pydantic BaseModel.
- Pass the class itself, not an instance.
- 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
- Always decorate row classes with @dataclass or inherit NamedTuple/BaseModel.
- Pass classes, never instances, as row_type.
- Avoid TypedDict for row_type; it is not accepted here.
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
- record_type must be a record type (dataclass, NamedTuple…
- Azure Blob container client must expose account_name and…
- Cannot specify both an S3 URI and a separate key. Pass…
- Cannot specify both row_factory and row_type
- Column cannot combine ZvecFtsType with ZvecType.
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)