{"record":{"id":"a6a547f1df551053","repo":"cocoindex-io/cocoindex","slug":"record-type-must-be-a-record-type-dataclass-name-a6a547","errorCode":null,"errorMessage":"record_type must be a record type (dataclass, NamedTuple, Pydantic model), got {type(record_type)}","messagePattern":"record_type must be a record type \\(dataclass, NamedTuple, Pydantic model\\), got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/surrealdb/_target.py","lineNumber":379,"sourceCode":"    async def from_class(\n        cls,\n        record_type: type[RowT],\n        *,\n        column_overrides: dict[str, SurrealType | res_schema.VectorSchemaProvider]\n        | None = None,\n    ) -> \"TableSchema[RowT]\":\n        \"\"\"\n        Create a TableSchema from a record type (dataclass, NamedTuple, or Pydantic model).\n\n        Python types are automatically mapped to SurrealDB types.\n\n        Args:\n            record_type: A record type (dataclass, NamedTuple, or Pydantic model).\n            column_overrides: Optional dict mapping field names to SurrealType or\n                              VectorSchemaProvider to override the default type mapping.\n        \"\"\"\n        if not is_record_type(record_type):\n            raise TypeError(\n                f\"record_type must be a record type (dataclass, NamedTuple, Pydantic model), \"\n                f\"got {type(record_type)}\"\n            )\n        columns = await cls._columns_from_record_type(record_type, column_overrides)\n        return cls(columns, row_type=record_type)\n\n    @staticmethod\n    async def _columns_from_record_type(\n        record_type: type,\n        column_overrides: dict[str, SurrealType | res_schema.VectorSchemaProvider]\n        | None,\n    ) -> dict[str, ColumnDef]:\n        \"\"\"Convert a record type to a dict of field name -> ColumnDef.\"\"\"\n        record_info = RecordType(record_type)\n        columns: dict[str, ColumnDef] = {}\n\n        for field in record_info.fields:\n            type_info = analyze_type_info(field.type_hint)","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/surrealdb/_target.py#L361-L397","documentation":"Surrealdb table targets are built from a 'record type' — a dataclass, NamedTuple, or Pydantic model — which supplies the row schema. `from_class` checks `is_record_type(record_type)` and refuses anything else so that column extraction has a well-defined schema to read from. Passing a plain dict, TypedDict, or arbitrary class triggers this TypeError.","triggerScenarios":"Calling `TableTarget.from_class(record_type=...)` (or the declare-table entry that delegates to it) with a plain `dict`, a `TypedDict`, a plain `class` without dataclass/NamedTuple/Pydantic decoration, a generic alias, or `None`.","commonSituations":"Developers coming from dict-based ORMs pass a `TypedDict` thinking it qualifies; forgetting the `@dataclass` decorator; passing a Pydantic model *class* where an instance-based schema was expected elsewhere; autocompletion picking the wrong `from_class` overload.","solutions":["Decorate the class with `@dataclasses.dataclass` (or make it a `typing.NamedTuple` subclass, or a Pydantic `BaseModel`) and pass that class.","Verify you are passing the class itself, not an instance (`MyRecord`, not `MyRecord(...)`).","If you were using a TypedDict, convert it to a dataclass with the same fields.","Print `type(record_type)` from the error message to confirm what was actually passed."],"exampleFix":"// before\nfrom typing import TypedDict\nclass User(TypedDict):\n    name: str\n    age: int\ntarget = await surrealdb.TableTarget.from_class(record_type=User)\n// after\nimport dataclasses\nclass User:\n    name: str\n    age: int\n\ntarget = await surrealdb.TableTarget.from_class(record_type=dataclasses.dataclass(User) if not dataclasses.is_dataclass(User) else User)","handlingStrategy":"type-guard","validationCode":"import dataclasses, typing\nfrom pydantic import BaseModel\n\ndef is_record_type(t) -> bool:\n    return dataclasses.is_dataclass(t) or (isinstance(t, type) and issubclass(t, typing.NamedTuple)) or (isinstance(t, type) and issubclass(t, BaseModel))\n\nassert is_record_type(MyRecord), f\"{type(MyRecord)} is not a dataclass/NamedTuple/Pydantic model\"","typeGuard":"def is_record_type(t: object) -> bool:\n    import dataclasses, typing\n    from pydantic import BaseModel\n    return (\n        dataclasses.is_dataclass(t)\n        or (isinstance(t, type) and issubclass(t, typing.NamedTuple))\n        or (isinstance(t, type) and issubclass(t, BaseModel))\n    )","tryCatchPattern":"try:\n    target = await surrealdb.TableTarget.from_class(record_type=record_type)\nexcept TypeError as e:\n    if \"record_type must be a record type\" in str(e):\n        raise ValueError(f\"Fix record_type definition: got {type(record_type)}\") from e\n    raise","preventionTips":["Always declare row schemas with @dataclass, typing.NamedTuple, or pydantic.BaseModel.","Pass the class, never an instance, to from_class.","Add a unit test asserting from_class accepts your schema class.","Avoid TypedDict for schema definitions passed to this API."],"tags":["python","type-error","schema","surrealdb"],"backgroundTag":"type-mismatch","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}