You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Models other than gemini are unable to make a tool_call , they expect a id parameter as string. #2621
Assistant tool_calls should include a non-null ID per call, and subsequent tool responses should reference that ID, allowing tool execution to proceed with OpenAI/Anthropic.
Impact
OpenAI and Anthropic models cannot execute tools in the ADK multi-agent flow.
Gemini models can execute tools successfully under the same setup.
@Kk12suthar thanks for the detailed report. I've confirmed the issue and the root cause is indeed the 'null' 'tool_call' ID for non-Gemini models.
As a temporary solution, you can pre-process the message history to inject a unique ID for each tool call before sending it to the model. Here is a Python snippet that demonstrates this fix.
'''
python
import uuid
for message in messages:
if message.get("role") == "assistant" and "tool_calls" in message:
for tool_call in message["tool_calls"]:
if tool_call.get("id") is None:
tool_call["id"] = f"call_{uuid.uuid4()}"
'''
The issue persists in v1.21.0 because remove_client_function_call_id() at functions.py:68 strips the IDs that were just added by populate_client_function_call_id().
The function removes IDs with 'adk-' prefix before sending conversation history to the model, which causes OpenAI/Anthropic to reject null IDs (Gemini tolerates this).
As a workaround until the fix is merged, monkey-patch remove_client_function_call_id to skip ID removal for non-Gemini providers, or use LiteLLM's format transformations to regenerate provider-specific IDs (OpenAI: call_{uuid}, Anthropic: toolu_{uuid}) before sending to the API.
The proper fix requires modifying functions.py to accept a model_provider parameter and only remove IDs for Gemini models.
I tested the fix suggested above, and actually, the problem is not in the ID management, but how AnthropicLlm processes tool responses.
The tool returns {"status": "success", "city": "London", "time": "..."}, but AnthropicLlm only handles responses with "content" or "result" keys. Without those, an empty string is sent to Claude.
For now I managed to solve the problem with this simple patch:
diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py
index 163fbe45..be385be3 100644
--- a/src/google/adk/models/anthropic_llm.py
+++ b/src/google/adk/models/anthropic_llm.py
@@ -18,6 +18,7 @@ from __future__ import annotations
import base64
from functools import cached_property
+import json
import logging
import os
from typing import Any
@@ -120,6 +121,13 @@ def part_to_message_block(
# ToolResultBlockParam content doesn't support list of dict. Converting
# to str to prevent anthropic.BadRequestError from being thrown.
content = str(response_data["result"])
+ # Handle any other response format (custom tool responses)
+ else:
+ # Serialize the entire response as JSON string for custom formats
+ try:
+ content = json.dumps(response_data, indent=2)
+ except (TypeError, ValueError):
+ content = str(response_data)
return anthropic_types.ToolResultBlockParam(
tool_use_id=part.function_response.id or "",
@maxa-bruno-hivert You've identified a second issue - AnthropicLlm's part_to_message_block only handles "content" and "result" keys (anthropic_llm.py:105-123), sending empty strings for custom tool response formats like {"status": "success", "city": "London"}.
Your patch to JSON-serialize unhandled response formats is correct and hasn't been applied in v1.21.0.
This is a separate bug from the tool_call ID issue and affects Anthropic specifically.
Both issues need fixing: (1) tool_call IDs being set to null by remove_client_function_call_id() in functions.py:68-90, and
(2) custom tool responses not being serialized for Anthropic.
We'll review your patch for the response serialization issue.
Can you also test if the ID issue affects your setup after applying your response format fix?
This will help us understand if both problems need addressing or if the response format was the primary blocker.
Activity
surajksharma07 commentedon Oct 26, 2025
@Kk12suthar thanks for the detailed report. I've confirmed the issue and the root cause is indeed the 'null' 'tool_call' ID for non-Gemini models.
As a temporary solution, you can pre-process the message history to inject a unique ID for each tool call before sending it to the model. Here is a Python snippet that demonstrates this fix.
Lmk if this works for you or not.
Thanks!
ayoubelqadi commentedon Dec 17, 2025
I have the same problem. I applied the fix you proposed but it doesn't work
surajksharma07 commentedon Dec 17, 2025
The issue persists in v1.21.0 because
remove_client_function_call_id()at functions.py:68 strips the IDs that were just added bypopulate_client_function_call_id().The function removes IDs with 'adk-' prefix before sending conversation history to the model, which causes OpenAI/Anthropic to reject null IDs (Gemini tolerates this).
As a workaround until the fix is merged, monkey-patch
remove_client_function_call_idto skip ID removal for non-Gemini providers, or use LiteLLM's format transformations to regenerate provider-specific IDs (OpenAI:call_{uuid}, Anthropic:toolu_{uuid}) before sending to the API.The proper fix requires modifying functions.py to accept a model_provider parameter and only remove IDs for Gemini models.
surajksharma07 commentedon Dec 17, 2025
@seanzhou1023 Please have a look into it.
maxa-bruno-hivert commentedon Dec 23, 2025
I tested the fix suggested above, and actually, the problem is not in the ID management, but how AnthropicLlm processes tool responses.
The tool returns {"status": "success", "city": "London", "time": "..."}, but AnthropicLlm only handles responses with "content" or "result" keys. Without those, an empty string is sent to Claude.
For now I managed to solve the problem with this simple patch:
surajksharma07 commentedon Jan 7, 2026
@maxa-bruno-hivert You've identified a second issue - AnthropicLlm's part_to_message_block only handles "content" and "result" keys (anthropic_llm.py:105-123), sending empty strings for custom tool response formats like {"status": "success", "city": "London"}.
Your patch to JSON-serialize unhandled response formats is correct and hasn't been applied in v1.21.0.
This is a separate bug from the tool_call ID issue and affects Anthropic specifically.
Both issues need fixing: (1) tool_call IDs being set to null by remove_client_function_call_id() in functions.py:68-90, and
(2) custom tool responses not being serialized for Anthropic.
We'll review your patch for the response serialization issue.
Can you also test if the ID issue affects your setup after applying your response format fix?
This will help us understand if both problems need addressing or if the response format was the primary blocker.
fix: preserve function call ids for litellm models
fix: preserve function call ids for litellm models