Skip to content

[LiteLlm] JSONDecodeError on malformed tool_call.function.arguments still crashes invocation at v2.0.0 (regression of #5008) #5896

@Ja-Crispy

Description

@Ja-Crispy

🔴 Required Information

Describe the Bug:

When the upstream model returns malformed JSON in tool_call.function.arguments (truncated strings, unclosed objects, partial streams committed to the message), google.adk.models.lite_llm._message_to_generate_content_response calls json.loads(tool_call.function.arguments or "{}") without a guard and raises json.JSONDecodeError. The exception propagates out of LiteLLM response parsing — upstream of any tool or callback boundary — so the entire invocation aborts and the user sees a generic streaming-error message rather than a recoverable tool-call retry.

This is the same bug as the previously closed #5008. That issue was closed after the original reporter said the upgrade had resolved it for them; a subsequent commenter (@NerdSmith) noted it still reproduced on 1.32.0, but the issue was not reopened. The crash site is unchanged on main at v2.0.0 (src/google/adk/models/lite_llm.py around _message_to_generate_content_response, the args=json.loads(tool_call.function.arguments or "{}") line).

Steps to Reproduce:

  1. Install google-adk==2.0.0 (or build from main) and litellm.
  2. Run any ADK flow that uses a LiteLLM-wrapped model and emits a function call.
  3. Cause the model to emit malformed JSON in tool_call.function.arguments — easiest in a test by constructing the message directly (see Minimal Reproduction Code below).
  4. Observe JSONDecodeError propagate out of _message_to_generate_content_response.

Expected Behavior:

ADK should treat malformed tool-argument JSON as a recoverable model error. Log a warning with the raw arguments and pass an empty dict (or equivalent) to the function-call Part so the downstream tool dispatch surfaces a structured bad_arguments-style error to the model. The agent loop survives and the model can retry with corrected JSON.

Observed Behavior:

File ".../google/adk/models/lite_llm.py", line 1730, in _message_to_generate_content_response
    args=json.loads(tool_call.function.arguments or "{}"),
File "/usr/local/lib/python3.12/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
File "/usr/local/lib/python3.12/json/decoder.py", line 354, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 2 (char 1)

Environment Details:

  • ADK Library Version: 2.0.0 (main, commit aa515125)
  • Desktop OS: macOS 25.3.0 (also reproduced on Linux in Cloud Run staging)
  • Python Version: 3.11.15 (also reproduced under 3.12)

Model Information:


🟡 Optional Information

Regression:

Filed as still-broken at 1.32.0 in the trailing comment on #5008, and confirmed still present at 2.0.0.

Logs:

See Observed Behavior above.

Additional Context:

The failure happens during LiteLLM response → ADK LlmResponse conversion, which sits upstream of before_tool_callback / after_tool_callback and the tool dispatch layer. So no callback-level mitigation is possible — the entire invocation aborts. From a production-operator perspective this is one of the harder ADK crashes to recover from: the trace lands in framework code, not user code, and the user-facing error is a generic stream failure with no information about the malformed tool call.

Minimal Reproduction Code:

from google.adk.models.lite_llm import _message_to_generate_content_response
from litellm import ChatCompletionAssistantMessage, ChatCompletionMessageToolCall
from litellm.types.utils import Function

message = ChatCompletionAssistantMessage(
    role="assistant",
    content=None,
    tool_calls=[
        ChatCompletionMessageToolCall(
            type="function",
            id="call_1",
            function=Function(name="demo_tool", arguments='{"city":"unterminated'),
        )
    ],
)

_message_to_generate_content_response(message)  # raises json.JSONDecodeError

How often has this issue occurred?:

  • Intermittently (<50%) — model-dependent, hits more often on long/streamed tool args under load.

I have a fix + unit tests ready as a PR: https://github.com/Ja-Crispy/adk-python/tree/fix-litellm-malformed-tool-args-json — happy to open it against this issue once it's triaged.

Metadata

Metadata

Assignees

Labels

models[Component] Issues related to model support

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions