Skip to content

fix(openai): treat non-dict JSON tool arguments as invalid tool calls#36052

Closed
Balaji Seshadri (gitbalaji) wants to merge 3 commits intolangchain-ai:masterfrom
gitbalaji:fix/openai-non-dict-tool-args-invalid
Closed

fix(openai): treat non-dict JSON tool arguments as invalid tool calls#36052
Balaji Seshadri (gitbalaji) wants to merge 3 commits intolangchain-ai:masterfrom
gitbalaji:fix/openai-non-dict-tool-args-invalid

Conversation

@gitbalaji
Copy link
Contributor

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 caught JSONDecodeError, so any of these non-dict results were passed directly as args in a tool_call entry. This causes a ValidationError in AIMessage because ToolCall.args is typed as dict[str, Any].

What changed

langchain_openai/chat_models/base.py — in _construct_lc_result_from_responses_api, after json.loads() succeeds, added an isinstance(args, dict) check that raises TypeError for non-dict results. TypeError is now caught alongside JSONDecodeError, routing the call to invalid_tool_calls with the raw arguments string and a descriptive error message.

Tests

Added test__construct_lc_result_from_responses_api_function_call_non_dict_json with 5 parametrized cases ([1,2,3], "a string", 42, true, null) — all now produce an invalid_tool_call entry instead of raising a ValidationError.


🤖 This PR was developed with assistance from Claude Code (Anthropic).

…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>
@github-actions github-actions bot added anthropic `langchain-anthropic` package issues & PRs fix For PRs that implement a fix integration PR made that is related to a provider partner package integration openai `langchain-openai` package issues & PRs size: S 50-199 LOC labels Mar 18, 2026
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 18, 2026

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 2 untouched benchmarks
🆕 3 new benchmarks
⏩ 34 skipped benchmarks1

Performance Changes

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

Open in CodSpeed

Footnotes

  1. 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.

  2. 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.

RoomWithOutRoof (Jah-yee) pushed a commit to Jah-yee/langchain that referenced this pull request Mar 18, 2026
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
@github-actions
Copy link

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:

  1. Comment on the linked issue to request assignment from a maintainer
  2. Once assigned, edit your PR description and the PR will be reopened automatically

@github-actions github-actions bot closed this Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

anthropic `langchain-anthropic` package issues & PRs external fix For PRs that implement a fix integration PR made that is related to a provider partner package integration missing-issue-link openai `langchain-openai` package issues & PRs size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-dict tool arguments should be set invalid

2 participants