Bug Report: 500 Internal Error with Multi-Turn Tool Use (gemini-2.5-pro-preview-03-25)

Environment:

  • Model: gemini-2.5-pro-preview-03-25
  • SDK: google-genai (Python)
  • API Call: client.models.generate_content

Problem Description:

We are consistently encountering a 500 INTERNAL Server Error when making calls to gemini-2.5-pro-preview-03-25 during a specific multi-turn tool use sequence.

Scenario:

The error occurs reliably during the API call that follows the second successful tool execution within a single user turn. The sequence is:

  1. Initial User Message.
  2. API Call 1: Model responds with a FunctionCall (e.g., for read_file). (Success)
  3. Tool Execution: read_file executes successfully.
  4. API Call 2: History includes User Msg, Model(Call A), Tool(Response A). Model responds with another FunctionCall (e.g., for think). (Success)
  5. Tool Execution: think executes successfully.
  6. API Call 3: History includes User Msg, Model(Call A), Tool(Response A), Model(Call B), Tool(Response B). This call consistently fails with a 500 INTERNAL error.

Observations:

  • The first API call and the second API call (after the first tool response) succeed without errors.
  • The issue started occurring after switching the model to gemini-2.5-pro-preview-03-25. Previous models (like flash) seemed to handle similar sequences.
  • We initially encountered “No candidates” errors due to incorrect FunctionResponse formatting (missing tool name), but these client-side issues have been resolved. The FunctionResponse parts are now being created correctly using the tool_use_id to find the name.
  • We have tried mapping our internal role="tool" (containing the result) to both Gemini’s role="user" and role="tool" for the FunctionResponse part – both lead to the 500 INTERNAL error on the third API call.
  • We have tried significantly truncating/simplifying the content within the result field of the FunctionResponse payloads, but the 500 INTERNAL error persists.

Relevant Code Snippet (Message Conversion):

This shows how we are currently constructing the FunctionResponse part (mapping our internal role="tool" to Gemini’s role="tool"). The logic to find tool_name using the ID from previous messages is included and works correctly based on logs.

# In function _convert_to_gemini_messages(standard_messages: List[Dict]) -> List[types.Content]:
# ... inside loop processing messages ...
        elif role == "tool":
            gemini_role = "tool" # Map internal 'tool' role to Gemini's 'tool' role
            if isinstance(std_content, list):
                # ... (loop through results 'res' in std_content) ...
                if res.get("type") == "tool_result":
                    tool_use_id_to_find = res.get("tool_use_id")
                    result_content = res.get("content")
                    is_error = res.get("is_error", False)
                    tool_name = None
                    # ... (Logic to find tool_name from preceding assistant message using tool_use_id_to_find) ...
                    if not tool_name:
                        # Log warning and skip
                        continue

                    # --- Payload Preparation ---
                    response_payload_value = result_content
                    # (Attempt JSON parsing if result_content is stringified JSON)
                    if isinstance(response_payload_value, str):
                         try:
                             parsed_content = json.loads(response_payload_value)
                             response_payload_value = parsed_content
                         except json.JSONDecodeError:
                             pass # Use as string if not JSON

                    response_payload = {"result": response_payload_value} if not is_error else {"error": str(response_payload_value)}
                    # --- End Payload Preparation ---

                    try:
                         gemini_parts.append(types.Part.from_function_response(name=tool_name, response=response_payload))
                         valid_responses += 1
                    except Exception as part_creation_error:
                         # Log error
                         pass
            # ... (rest of tool role handling) ...
        # ... (rest of message conversion) ...

# Ensure user message buffering/merging is handled correctly if applicable
# ...

Sample History Structure Sent During Failing Call (API Call 3):

[
  # Content object for Initial User Context/Input (role="user")
  Content(role='user', parts=[Part(text='...')]),
  # Content object for Model's First Tool Call (role="model")
  Content(role='model', parts=[Part(text='Okay, reading file...'), Part(function_call=FunctionCall(name='read_file', args={'path': '...'}))]),
  # Content object for First Tool Response (role="tool")
  Content(role='tool', parts=[Part(function_response=FunctionResponse(name='read_file', response={'result': '...file content...'}))]),
  # Content object for Model's Second Tool Call (role="model")
  Content(role='model', parts=[Part(text='Okay, thinking...'), Part(function_call=FunctionCall(name='think', args={'thought': '...'}))]),
  # Content object for Second Tool Response (role="tool")
  Content(role='tool', parts=[Part(function_response=FunctionResponse(name='think', response={'result': 'Thought processed.'}))]) # Simplified payload example
]

Exact Error:

google.genai.errors.ServerError: 500 INTERNAL. {'error': {'code': 500, 'message': 'An internal error has occurred. Please retry or report in https://developers.generativeai.google/guide/troubleshooting', 'status': 'INTERNAL'}}

Question:

Is this a known limitation or potential bug with gemini-2.5-pro-preview-03-25 when handling multiple FunctionResponse parts in the history? Our client-side formatting seems correct now, and the error points to an internal server issue triggered by this specific state.

Any insights or suggestions would be greatly appreciated. Thank you!


Hey @Kabalan_Research , Initially, I received some warnings when trying to include multiple function responses, especially with gemini-2.5-pro-preview-03-25. I’m not sure if it was a temporary server-side issue, but now, when I manually include the multiple function responses one by one in the message history, it seems to work as expected. Here is the colab gist for your reference. Thanks.

1 Like