Skip to content

Models other than gemini are unable to make a tool_call , they expect a id parameter as string. #2621

Closed

Description

@Kk12suthar

Bug Report: Tool calls fail on OpenAI/Anthropic models in Google ADK multi‑agent systems; only Gemini works

Summary

  • Tool calls made through Google ADK multi-agent system fail for OpenAI and Anthropic models due to missing/invalid tool_call IDs in assistant messages.
  • The same flow works with Gemini models.

Environment

  • Python: 3.13.3
  • Google ADK: tested 1.6.1, 1.8.0, 1.11.0 (same behavior)
  • Models:
    • OpenAI: gpt-4o-mini
    • Anthropic: claude-sonnet-4-20250514
    • Gemini: gemini-2.5-flash (works)
  • LiteLLM: 1.75.8 (latest)
  • OS: Windows

Steps to Reproduce

  1. Configure a Google ADK multi-agent workflow where assistant messages issue tool calls
  2. Set model to OpenAI (gpt-4o-mini) or Anthropic (claude-sonnet-4-20250514).
  3. Start the app and trigger a step that requires a tool call.
  4. Observe request failure from the LLM provider via LiteLLM.

Actual Behavior

  • OpenAI:
    • litellm.BadRequestError: OpenAIException - Invalid type for 'messages[4].tool_calls[0].id': expected a string, but got null instead.
  • Anthropic:
    • litellm.BadRequestError: AnthropicException - 'tool_call_id'
  • Tool calls contain id=null or omit tool_call_id, causing provider validation errors.

Expected Behavior

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

Activity

  1. added theissue type on Aug 20, 2025
  2. surajksharma07 commented on Oct 26, 2025

    @surajksharma07
    Collaborator

    @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()}"
    '''
    

    Lmk if this works for you or not.
    Thanks!

  3. ayoubelqadi commented on Dec 17, 2025

    @ayoubelqadi

    I have the same problem. I applied the fix you proposed but it doesn't work

  4. surajksharma07 commented on Dec 17, 2025

    @surajksharma07
    Collaborator

    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.

  5. surajksharma07 commented on Dec 17, 2025

    @surajksharma07
    Collaborator

    @seanzhou1023 Please have a look into it.

  6. maxa-bruno-hivert commented on Dec 23, 2025

    @maxa-bruno-hivert

    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 "",
    
  7. surajksharma07 commented on Jan 7, 2026

    @surajksharma07
    Collaborator

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

  8. added a commit that references this issue on Jun 16, 2026
    1ad348d
  9. self-assigned this
    on Jun 16, 2026
  10. added a commit that references this issue on Aug 11, 2026
    5d18b07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

models[Component] This issue is related to model supportneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions