{"record":{"id":"11d0d697396732a3","repo":"langchain-ai/langchain","slug":"tool-arguments-must-be-specified-as-a-dict-receiv","errorCode":null,"errorMessage":"Tool arguments must be specified as a dict, received: {res['args']}","messagePattern":"Tool arguments must be specified as a dict, received: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/output_parsers/openai_tools.py","lineNumber":356,"sourceCode":"        name_dict_v2: dict[str, TypeBaseModel] = {\n            tool.model_config.get(\"title\") or tool.__name__: tool\n            for tool in self.tools\n            if issubclass(tool, BaseModel)\n        }\n        name_dict_v1: dict[str, TypeBaseModel] = {\n            tool.__name__: tool for tool in self.tools if issubclass(tool, BaseModelV1)\n        }\n        name_dict: dict[str, TypeBaseModel] = {**name_dict_v2, **name_dict_v1}\n        pydantic_objects = []\n        for res in json_results:\n            if not isinstance(res[\"args\"], dict):\n                if partial:\n                    continue\n                msg = (\n                    f\"Tool arguments must be specified as a dict, received: \"\n                    f\"{res['args']}\"\n                )\n                raise ValueError(msg)\n\n            try:\n                tool = name_dict[res[\"type\"]]\n            except KeyError as e:\n                available = \", \".join(name_dict.keys()) or \"<no_tools>\"\n                msg = (\n                    f\"Unknown tool type: {res['type']!r}. Available tools: {available}\"\n                )\n                raise OutputParserException(msg) from e\n\n            try:\n                pydantic_objects.append(tool(**res[\"args\"]))\n            except (ValidationError, ValueError):\n                if partial:\n                    continue\n                has_max_tokens_stop_reason = any(\n                    generation.message.response_metadata.get(\"stop_reason\")\n                    == \"max_tokens\"","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/output_parsers/openai_tools.py#L338-L374","documentation":"Raised by PydanticToolsParser.parse_result (non-partial mode) when a parsed tool result's 'args' is not a dict — e.g. the model returned a JSON list, string, or number as the tool arguments. Constructing tool(**res['args']) requires keyword arguments, so a non-mapping args payload is a hard ValueError.","triggerScenarios":"Model emits tool arguments as a JSON array or scalar (e.g. \"[1,2]\" or \"\\\"text\\\"\") instead of an object; partial=False so the parser cannot silently skip the bad entry.","commonSituations":"Tool schemas whose parameters are an array at top level (invalid per OpenAI spec but sometimes written); models trained to emit positional arguments; degenerate outputs from small local models.","solutions":["Define the tool schema so top-level parameters are a JSON object (properties/type: object), not an array","Improve the prompt/schema so the model always emits an object for arguments","Catch ValueError and retry the call, or pre-validate res['args'] and skip/repair non-dict entries"],"exampleFix":"# before\nclass SearchArgs(BaseModel):\n    query: str\n# model emitted args as a bare string \"cat videos\"\n\n# after\n# enforce object arguments in the tool description/system prompt:\nsystem = \"Tool arguments MUST be a JSON object, e.g. {\\\"query\\\": \\\"...\\\"}\"","handlingStrategy":"validation","validationCode":"for res in json_results:\n    if not isinstance(res[\"args\"], dict):\n        continue  # or repair: {\"value\": res[\"args\"]}\n# only then call the parser","typeGuard":"def has_dict_args(res: dict) -> bool:\n    return isinstance(res.get(\"args\"), dict)","tryCatchPattern":"try:\n    objs = parser.parse_result(result)\nexcept ValueError as e:\n    if \"must be specified as a dict\" in str(e):\n        objs = [o for o in parser.parse_result(result, partial=True) if o is not None]  # skip bad entries","preventionTips":["Design tool schemas with top-level object parameters","Show argument examples in the tool description"],"tags":["tool-calling","pydantic","validation","output-parsing"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}