langchain-ai/langchain · error · TypeError

args_schema must be a subclass of pydantic BaseModel or a JS

Error message

args_schema must be a subclass of pydantic BaseModel or a JSON schema dict. Got: {kwargs['args_schema']}.

What it means

`BaseTool.__init__` (via `validate`) rejects an `args_schema` value that is neither a Pydantic `BaseModel` subclass nor a plain dict (JSON schema). This catches typos like passing an instance instead of the class, or a non-schema object, before the tool ever runs.

Source

Thrown at libs/core/langchain_core/tools/base.py:591

    def __init__(self, **kwargs: Any) -> None:
        """Initialize the tool.

        Raises:
            TypeError: If `args_schema` is not a subclass of pydantic `BaseModel` or
                `dict`.
        """
        if (
            "args_schema" in kwargs
            and kwargs["args_schema"] is not None
            and not is_basemodel_subclass(kwargs["args_schema"])
            and not isinstance(kwargs["args_schema"], dict)
        ):
            msg = (
                "args_schema must be a subclass of pydantic BaseModel or "
                f"a JSON schema dict. Got: {kwargs['args_schema']}."
            )
            raise TypeError(msg)
        super().__init__(**kwargs)

    model_config = ConfigDict(
        arbitrary_types_allowed=True,
    )

    @property
    def is_single_input(self) -> bool:
        """Check if the tool accepts only a single input argument.

        Returns:
            `True` if the tool has only one input argument, `False` otherwise.
        """
        keys = {k for k in self.args if k != "kwargs"}
        return len(keys) == 1

    @property
    def args(self) -> dict[str, Any]:

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Pass the Pydantic model class itself: `args_schema=MySchema` (no parentheses).
  2. For JSON schemas, pass the parsed dict: `args_schema={'type': 'object', 'properties': {...}}`.
  3. If you have a TypedDict/dataclass, convert it to a Pydantic model first.

Example fix

# before
tool = Tool(name='search', func=fn, description='d', args_schema=SearchArgs())
# after
tool = Tool(name='search', func=fn, description='d', args_schema=SearchArgs)
Defensive patterns

Strategy: type-guard

Validate before calling

from pydantic import BaseModel

def valid_args_schema(x) -> bool:
    return x is None or (isinstance(x, type) and issubclass(x, BaseModel)) or (
        isinstance(x, dict) and isinstance(x.get('type', 'object'), str)
    )

assert valid_args_schema(schema_value)

Type guard

from pydantic import BaseModel
import inspect

def is_basemodel_subclass(x) -> bool:
    return inspect.isclass(x) and issubclass(x, BaseModel)

Try / catch

try:
    t = Tool(name='t', func=fn, description='d', args_schema=schema)
except TypeError as e:
    if 'args_schema must be a subclass' in str(e):
        t = Tool(name='t', func=fn, description='d',
                 args_schema=schema if isinstance(schema, type) else dict(schema))
    else:
        raise

Prevention

When it happens

Trigger: `Tool(name='t', ..., args_schema=MySchema())` (instance instead of class); passing a string, a dataclass, or a Pydantic `BaseModel`-like object from another library as `args_schema`; passing a `TypedDict` or JSON string.

Common situations: Confusing the schema class with an instance; migrating from old dataclass-based tool schemas; passing a serialized schema (JSON string) instead of the parsed dict; passing `TypedDict` classes where only Pydantic models or raw JSON-schema dicts are supported.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/e37660d886a33f42. Report an issue: GitHub.