{"record":{"id":"1692696d353677fa","repo":"langchain-ai/langchain","slug":"string-tool-inputs-are-not-allowed-when-using-tool","errorCode":null,"errorMessage":"String tool inputs are not allowed when using tools with JSON schema args_schema.","messagePattern":"String tool inputs are not allowed when using tools with JSON schema args_schema\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/tools/base.py","lineNumber":807,"sourceCode":"        Returns:\n            The parsed and validated input.\n\n        Raises:\n            ValueError: If `string` input is provided with JSON schema `args_schema`.\n            ValueError: If `InjectedToolCallId` is required but `tool_call_id` is not\n                provided.\n            TypeError: If `args_schema` is not a Pydantic `BaseModel` or dict.\n        \"\"\"\n        input_args = self.args_schema\n\n        if isinstance(tool_input, str):\n            if input_args is not None:\n                if isinstance(input_args, dict):\n                    msg = (\n                        \"String tool inputs are not allowed when \"\n                        \"using tools with JSON schema args_schema.\"\n                    )\n                    raise ValueError(msg)\n                key_ = next(iter(get_fields(input_args).keys()))\n                if issubclass(input_args, BaseModel):\n                    input_args.model_validate({key_: tool_input})\n                elif issubclass(input_args, BaseModelV1):\n                    input_args.parse_obj({key_: tool_input})\n                else:\n                    msg = f\"args_schema must be a Pydantic BaseModel, got {input_args}\"  # type: ignore[unreachable]\n                    raise TypeError(msg)\n            return tool_input\n\n        if input_args is not None:\n            if isinstance(input_args, dict):\n                return tool_input\n            result: BaseModel | BaseModelV1\n            if issubclass(input_args, BaseModel):\n                # Check args_schema for InjectedToolCallId\n                for k, v in get_all_basemodel_annotations(input_args).items():\n                    if _is_injected_arg_type(v, injected_type=InjectedToolCallId):","sourceCodeStart":789,"sourceCodeEnd":825,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/tools/base.py#L789-L825","documentation":"When a tool built with a JSON-schema dict `args_schema` is invoked with a plain string, `Tool.run`/validation refuses it: JSON-schema-dict tools have no single-field convention to map the string onto, unlike single-field Pydantic tools where the string is assigned to the lone field. The ValueError is raised during input validation before `_run` executes.","triggerScenarios":"`my_tool.invoke('plain string query')` where `my_tool` was created with `args_schema={'type': 'object', ...}`; agents calling a JSON-schema tool with `tool_input` as a bare string (older models sometimes emit single-string tool calls).","commonSituations":"Chat models that emit `{'args': 'just a string'}` for single-arg tools; mixing Pydantic-arg tools (string input tolerated) with dict-schema tools (strict); hand-built tools whose schema dict defines multiple properties but the caller passes a string.","solutions":["Invoke with a dict matching the schema: `my_tool.invoke({'query': '...'})`.","If a single-string call must work, use a Pydantic single-field `args_schema` model instead of a raw JSON-schema dict (strings are then mapped to that one field).","Post-process malformed model tool calls (string args) into dicts before dispatching to the tool."],"exampleFix":"# before\n@tool\ndef lookup(q: str) -> str:\n    \"\"\"Look up.\"\"\"\n    ...\njson_tool = Tool(..., args_schema={'type':'object','properties':{'q':{'type':'string'}},'required':['q']})\njson_tool.invoke('alice')  # ValueError\n# after\njson_tool.invoke({'q': 'alice'})","handlingStrategy":"type-guard","validationCode":"def coerce_tool_input(tool, tool_input):\n    if isinstance(tool_input, str) and isinstance(tool.args_schema, dict):\n        props = tool.args_schema.get('properties', {})\n        if len(props) == 1:\n            return {next(iter(props)): tool_input}\n        raise ValueError('dict-schema tool needs full dict input')\n    return tool_input\n\ntool.invoke(coerce_tool_input(tool, raw_input))","typeGuard":"def accepts_string_input(tool) -> bool:\n    schema = tool.args_schema\n    return not isinstance(schema, dict) and (\n        schema is None or len(getattr(schema, 'model_fields', getattr(schema, '__fields__', {}))) <= 1\n    )","tryCatchPattern":"try:\n    out = tool.invoke(tool_input)\nexcept ValueError as e:\n    if 'String tool inputs are not allowed' in str(e):\n        out = tool.invoke({'query': tool_input})  # map string to schema field\n    else:\n        raise","preventionTips":["Always invoke tools with an args dict, not a bare string.","Prefer Pydantic args_schema over raw JSON-schema dicts for single-arg tools.","Sanitize model tool calls whose args are strings before dispatch."],"tags":["tool","json-schema","input-validation","string-input"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}