langchain - ✅(Solved) Fix core: default_tool_parser crashes on malformed function dicts, silently drops all valid tool calls [2 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#36679Fetched 2026-04-12 13:24:04
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
commented ×1cross-referenced ×1labeled ×1

default_tool_parser and default_tool_chunk_parser in langchain_core/messages/tool.py crash with unhandled KeyError/TypeError when a raw tool call has a malformed function field.

Root cause: Both functions check if "function" not in raw_tool_call but do not validate that the value is actually a dict with the expected keys. They use direct indexing (raw_tool_call["function"]["name"]) instead of .get().

Impact: When ANY single tool call in a list is malformed, the exception kills the entire parsing loop. The caller in ai.py catches Exception broadly, so ALL tool calls (including valid ones) are silently dropped. This is problematic because:

  1. The function is documented as Best-effort parsing but is actually all-or-nothing
  2. default_tool_parser already has an invalid_tool_calls return list for exactly this purpose, but malformed function dicts never reach it

Affected functions:

  • default_tool_parser (line 361): function_name = raw_tool_call["function"]["name"]
  • default_tool_chunk_parser (lines 405-406): tool_call["function"]["arguments"] and tool_call["function"]["name"]

Error Message

from langchain_core.messages.tool import default_tool_parser, default_tool_chunk_parser

Case 1: function value is None

raw = [{"function": None, "id": "call_1"}] default_tool_parser(raw) # TypeError

Case 2: function dict missing keys

raw = [{"function": {}, "id": "call_1"}] default_tool_parser(raw) # KeyError: name

Case 3: One malformed call drops ALL valid calls

raw = [ {"function": None, "id": "bad"}, {"function": {"name": "good_tool", "arguments": "{"a": 1}"}, "id": "good"}, ] default_tool_parser(raw) # Crashes on first item, good call never parsed

Root Cause

Root cause: Both functions check if "function" not in raw_tool_call but do not validate that the value is actually a dict with the expected keys. They use direct indexing (raw_tool_call["function"]["name"]) instead of .get().

Fix Action

Fixed

PR fix notes

PR #36680: core: handle malformed function dicts in tool call parsers

Description (problem / solution / changelog)

Description

Fixes #36679

default_tool_parser and default_tool_chunk_parser crash with unhandled KeyError/TypeError when a raw tool call has a malformed function field (None, non-dict, or missing name/arguments keys).

The real problem: One malformed tool call kills the entire parsing loop, and the broad except Exception in ai.py silently drops ALL tool calls including valid ones. A function documented as "Best-effort parsing" should not be all-or-nothing.

Changes

default_tool_parser

  • Validate function is a dict before accessing keys
  • Use .get() for name and arguments instead of direct indexing
  • Route non-dict function values to invalid_tool_calls with a descriptive error
  • Catch TypeError alongside JSONDecodeError (handles arguments: null)

default_tool_chunk_parser

  • Validate function is a dict before accessing keys
  • Use .get() for name and arguments
  • Treat non-dict function values same as missing function (args=None, name=None)

Tests

  • Added 16 unit tests in test_tool_parsers.py covering:
    • Valid tool calls (baseline)
    • function: null
    • function: {} (empty dict)
    • function: "string" (wrong type)
    • Missing name key
    • Missing arguments key
    • arguments: null
    • Mixed valid + malformed in same list (key test)

Reproduction

from langchain_core.messages.tool import default_tool_parser

# Before fix: crashes, drops ALL tool calls
default_tool_parser([{"function": None, "id": "1"}])  # TypeError
default_tool_parser([{"function": {}, "id": "1"}])    # KeyError

# After fix: gracefully routes to invalid_tool_calls
tool_calls, invalid = default_tool_parser([{"function": None, "id": "1"}])
assert len(tool_calls) == 0
assert len(invalid) == 1

Changed files

  • libs/core/langchain_core/messages/tool.py (modified, +22/-6)
  • libs/core/tests/unit_tests/messages/test_tool_parsers.py (added, +161/-0)

Code Example

from langchain_core.messages.tool import default_tool_parser, default_tool_chunk_parser

# Case 1: function value is None
raw = [{"function": None, "id": "call_1"}]
default_tool_parser(raw)  # TypeError

# Case 2: function dict missing keys
raw = [{"function": {}, "id": "call_1"}]
default_tool_parser(raw)  # KeyError: name

# Case 3: One malformed call drops ALL valid calls
raw = [
    {"function": None, "id": "bad"},
    {"function": {"name": "good_tool", "arguments": "{\"a\": 1}"}, "id": "good"},
]
default_tool_parser(raw)  # Crashes on first item, good call never parsed

---

TypeError: NoneType object is not subscriptable
KeyError: name
RAW_BUFFERClick to expand / collapse

Checked other resources

  • I searched existing issues and this has not been reported before

Example Code

from langchain_core.messages.tool import default_tool_parser, default_tool_chunk_parser

# Case 1: function value is None
raw = [{"function": None, "id": "call_1"}]
default_tool_parser(raw)  # TypeError

# Case 2: function dict missing keys
raw = [{"function": {}, "id": "call_1"}]
default_tool_parser(raw)  # KeyError: name

# Case 3: One malformed call drops ALL valid calls
raw = [
    {"function": None, "id": "bad"},
    {"function": {"name": "good_tool", "arguments": "{\"a\": 1}"}, "id": "good"},
]
default_tool_parser(raw)  # Crashes on first item, good call never parsed

Error Message and Stack Trace

TypeError: NoneType object is not subscriptable
KeyError: name

Description

default_tool_parser and default_tool_chunk_parser in langchain_core/messages/tool.py crash with unhandled KeyError/TypeError when a raw tool call has a malformed function field.

Root cause: Both functions check if "function" not in raw_tool_call but do not validate that the value is actually a dict with the expected keys. They use direct indexing (raw_tool_call["function"]["name"]) instead of .get().

Impact: When ANY single tool call in a list is malformed, the exception kills the entire parsing loop. The caller in ai.py catches Exception broadly, so ALL tool calls (including valid ones) are silently dropped. This is problematic because:

  1. The function is documented as Best-effort parsing but is actually all-or-nothing
  2. default_tool_parser already has an invalid_tool_calls return list for exactly this purpose, but malformed function dicts never reach it

Affected functions:

  • default_tool_parser (line 361): function_name = raw_tool_call["function"]["name"]
  • default_tool_chunk_parser (lines 405-406): tool_call["function"]["arguments"] and tool_call["function"]["name"]

System Info

langchain-core latest (main branch as of 2025-07-12)

extent analysis

TL;DR

Modify default_tool_parser and default_tool_chunk_parser to validate the function field and handle missing or malformed data using the .get() method.

Guidance

  • Validate the function field in default_tool_parser and default_tool_chunk_parser to ensure it is a dictionary with the expected keys before attempting to access its values.
  • Use the .get() method to safely retrieve values from the function dictionary, avoiding KeyError exceptions.
  • Modify the parsing loop to continue processing valid tool calls even if a single malformed call is encountered, utilizing the invalid_tool_calls return list as intended.
  • Review the documentation for default_tool_parser to reflect its actual behavior and consider updating it to match the intended best-effort parsing approach.

Example

function_name = raw_tool_call["function"].get("name")
if function_name is None:
    # Handle missing or malformed function name
    invalid_tool_calls.append(raw_tool_call)
    continue

Notes

The provided solution focuses on modifying the default_tool_parser and default_tool_chunk_parser functions to improve their robustness against malformed input data. However, a comprehensive solution might require additional changes, such as updating the documentation and potentially adjusting the error handling in the caller function (ai.py) to better handle exceptions.

Recommendation

Apply workaround: Modify the default_tool_parser and default_tool_chunk_parser functions as described to improve their handling of malformed input data and to align with the intended best-effort parsing behavior.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING