fix(openai): treat non-dict JSON tool arguments as invalid tool calls#36052
fix(openai): treat non-dict JSON tool arguments as invalid tool calls#36052Balaji Seshadri (gitbalaji) wants to merge 3 commits intolangchain-ai:masterfrom
Conversation
…re batch A bare `except KeyError: pass` around the full list comprehension in `_convert_delta_to_message_chunk` caused all tool call chunks to be silently discarded whenever a single item was missing the `function` or `index` key. Move the try/except inside the loop so only the malformed entry is skipped and valid chunks are preserved. Fixes langchain-ai#35782 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dleware variants Both `_StateClaudeFileToolMiddleware._handle_rename` and `_FilesystemClaudeFileToolMiddleware._handle_rename` were reading `args["old_path"]`, but the dispatch function builds the args dict with `args["path"]` as the source path key — causing a `KeyError` on every rename command. Closes langchain-ai#35852 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`json.loads()` can return non-dict types (list, string, number, boolean, null) for valid JSON. Previously only `JSONDecodeError` was caught, so non-dict results were passed directly as `args` in a `tool_call`, causing a `ValidationError` in `AIMessage` since `ToolCall.args` requires `dict[str, Any]`. Fixes langchain-ai#35990 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| 🆕 | WallTime | test_init_time |
N/A | 4 ms | N/A |
| 🆕 | WallTime | test_init_time |
N/A | 4 ms | N/A |
| 🆕 | WallTime | test_init_time |
N/A | 484.1 ms | N/A |
Comparing gitbalaji:fix/openai-non-dict-tool-args-invalid (bcdc073) with master (07fa576)2
Footnotes
-
34 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
master(67f5e31) during the generation of this report, so 07fa576 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
When json.loads() parses valid JSON that is not a dict (arrays, strings, numbers, booleans, or null), the result was passed directly as args in a tool_call entry, causing ValidationError in AIMessage because ToolCall.args is typed as dict[str, Any]. This fix adds isinstance(args, dict) check after json.loads() succeeds. Non-dict results now route to invalid_tool_calls with a descriptive error message. Also catches TypeError alongside JSONDecodeError for consistency. Fixes langchain-ai#36052
|
This PR has been automatically closed because you are not assigned to the linked issue. External contributors must be assigned to an issue before opening a PR for it. Please:
|
Fixes #35990
Why
json.loads()successfully parses valid JSON that is not a dict — arrays, strings, numbers, booleans, and null are all valid JSON. The Responses API tool call handler only caughtJSONDecodeError, so any of these non-dict results were passed directly asargsin atool_callentry. This causes aValidationErrorinAIMessagebecauseToolCall.argsis typed asdict[str, Any].What changed
langchain_openai/chat_models/base.py— in_construct_lc_result_from_responses_api, afterjson.loads()succeeds, added anisinstance(args, dict)check that raisesTypeErrorfor non-dict results.TypeErroris now caught alongsideJSONDecodeError, routing the call toinvalid_tool_callswith the raw arguments string and a descriptive error message.Tests
Added
test__construct_lc_result_from_responses_api_function_call_non_dict_jsonwith 5 parametrized cases ([1,2,3],"a string",42,true,null) — all now produce aninvalid_tool_callentry instead of raising aValidationError.