{"record":{"id":"8597d6cc5ace9e55","repo":"langchain-ai/langchain","slug":"expected-a-pydantic-model-got-model","errorCode":null,"errorMessage":"Expected a Pydantic model. Got {model}","messagePattern":"Expected a Pydantic model\\. Got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/utils/pydantic.py","lineNumber":347,"sourceCode":"def get_fields(\n    model: type[BaseModel | BaseModelV1] | BaseModel | BaseModelV1,\n) -> dict[str, FieldInfoV2] | dict[str, ModelField]:\n    \"\"\"Return the field names of a Pydantic model.\n\n    Args:\n        model: The Pydantic model or instance.\n\n    Raises:\n        TypeError: If the model is not a Pydantic model.\n    \"\"\"\n    if not isinstance(model, type):\n        model = type(model)\n    if issubclass(model, BaseModel):\n        return model.model_fields\n    if issubclass(model, BaseModelV1):\n        return model.__fields__\n    msg = f\"Expected a Pydantic model. Got {model}\"  # type: ignore[unreachable]\n    raise TypeError(msg)\n\n\ndef model_json_schema(model: TypeBaseModel) -> dict[str, Any]:\n    \"\"\"Return the JSON schema of a Pydantic model class of either major version.\n\n    Dispatches to the correct method for Pydantic v1 (`schema`) or v2\n    (`model_json_schema`), so callers holding a `TypeBaseModel` don't have to\n    branch on the model's version themselves.\n\n    Args:\n        model: The Pydantic model class.\n\n    Raises:\n        TypeError: If the model is not a Pydantic model class.\n    \"\"\"\n    if issubclass(model, BaseModel):\n        return model.model_json_schema()\n    if issubclass(model, BaseModelV1):","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/utils/pydantic.py#L329-L365","documentation":"Raised by `get_fields` in `langchain_core.utils.pydantic` when the argument is neither a pydantic v1 nor v2 `BaseModel` (class or instance). The utility introspects `.model_fields` (v2) or `__fields__` (v1); anything else — dataclasses, TypedDicts, plain classes, primitives — is rejected with `TypeError`. It typically surfaces indirectly when passing a non-pydantic schema object into LangChain APIs that expect a pydantic model.","triggerScenarios":"Calling `get_fields(obj)` (directly or via APIs that derive field schemas from models, such as tool/structured-output argument parsing) with a dataclass, `TypedDict`, `NamedTuple`, or arbitrary class. Note the function first does `type(model)` on instances, so instances of valid models are fine; only non-model types fail.","commonSituations":"Migrating code from dataclass-based tools to pydantic-based ones; passing a `TypedDict` where LangChain expects `BaseModel` (e.g. `with_structured_output`, tool args); duck-typed fakes/mocks in tests that are not pydantic models; mixing pydantic v1/v2 where the object is actually an unrelated class.","solutions":["Convert the argument to a pydantic model: decorate dataclasses with `pydantic.dataclasses.dataclass` won't help here — define a `BaseModel` subclass with the same fields.","If using TypedDict, switch to a pydantic `BaseModel` (or use an API path that accepts JSON Schema directly).","If the value may legitimately vary, branch on `isinstance(obj, BaseModel)` (and pydantic.v1 `BaseModel`) before calling."],"exampleFix":"# before\nfrom dataclasses import dataclass\n@dataclass\nclass Args:\n    query: str\nget_fields(Args)  # TypeError: Expected a Pydantic model.\n\n# after\nfrom pydantic import BaseModel\nclass Args(BaseModel):\n    query: str\nget_fields(Args)","handlingStrategy":"type-guard","validationCode":"from pydantic import BaseModel\nfrom pydantic.v1 import BaseModel as BaseModelV1\n\ndef has_fields(obj: Any) -> bool:\n    cls = obj if isinstance(obj, type) else type(obj)\n    return issubclass(cls, (BaseModel, BaseModelV1))","typeGuard":"from pydantic import BaseModel\nfrom pydantic.v1 import BaseModel as BaseModelV1\n\ndef is_pydantic_model(obj: Any) -> TypeGuard[type[BaseModel] | type[BaseModelV1]]:\n    cls = obj if isinstance(obj, type) else type(obj)\n    return issubclass(cls, (BaseModel, BaseModelV1))","tryCatchPattern":"try:\n    fields = get_fields(obj)\nexcept TypeError:\n    fields = None  # or convert obj to a BaseModel first","preventionTips":["Type tool/structured-output args as TypeBaseModel, not Any, so mypy catches misuse.","Convert dataclass schemas to BaseModel subclasses at the boundary.","In tests, use real pydantic models rather than duck-typed fakes."],"tags":["pydantic","type-error","schema"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}