From 923b5f9b41abcb5ada67007c0f6c6ac2fceae1e8 Mon Sep 17 00:00:00 2001 From: Aftab Date: Thu, 24 Sep 2026 06:21:40 +0530 Subject: [PATCH] fix(anthropic): unwrap double-encoded tool_call arguments for Anthropic providers When agent frameworks store/load tool call history they sometimes apply json.dumps twice to function.arguments, producing a JSON string whose first decode still returns a str. Anthropic tool_use.input must be an object; the extra string layer causes Azure AI Anthropic to return: messages.x.content.y.tool_use.input: Input should be an object parse_tool_call_arguments now detects this case: when json.loads() returns a str it attempts one additional decode and logs a warning naming the tool so callers can trace the source of the double-encoding. Fixes #42739 Signed-off-by: Aftabbs --- .../prompt_templates/common_utils.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/litellm/litellm_core_utils/prompt_templates/common_utils.py b/litellm/litellm_core_utils/prompt_templates/common_utils.py index 6c45622649f..cdc8da07321 100644 --- a/litellm/litellm_core_utils/prompt_templates/common_utils.py +++ b/litellm/litellm_core_utils/prompt_templates/common_utils.py @@ -1,4 +1,4 @@ -""" +""" Common utility functions used for translating messages across providers """ @@ -2382,7 +2382,24 @@ def parse_tool_call_arguments( return {} try: - return json.loads(arguments) + parsed = json.loads(arguments) + if isinstance(parsed, str): + # Double-encoded JSON: json.dumps applied twice by the caller (common + # in agent frameworks that serialise tool arguments through a store). + # Anthropic's tool_use.input must be an object, not a string, so + # unwrap one extra layer and warn so callers can fix the root cause. + verbose_logger.warning( + "Tool call arguments for tool '%s' (%s) decoded to a string " + "instead of an object — double-encoded JSON detected. " + "Attempting second decode.", + tool_name or "", + context or "unknown context", + ) + try: + parsed = json.loads(parsed) + except json.JSONDecodeError: + pass + return parsed except json.JSONDecodeError as original_error: repaired: Final = _attempt_json_repair(arguments) if repaired is not None: