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:
- Initial User Message.
- API Call 1: Model responds with a
FunctionCall
(e.g., forread_file
). (Success) - Tool Execution:
read_file
executes successfully. - API Call 2: History includes User Msg, Model(Call A), Tool(Response A). Model responds with another
FunctionCall
(e.g., forthink
). (Success) - Tool Execution:
think
executes successfully. - 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. TheFunctionResponse
parts are now being created correctly using thetool_use_id
to find the name. - We have tried mapping our internal
role="tool"
(containing the result) to both Gemini’srole="user"
androle="tool"
for theFunctionResponse
part – both lead to the500 INTERNAL
error on the third API call. - We have tried significantly truncating/simplifying the content within the
result
field of theFunctionResponse
payloads, but the500 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!