Issue with Gemini 2.5 Pro (Vertex AI): Looping / Incorrect Behavior on Sequential Tool Calls (read_file -> write_file) in Single Turn

Subject: Issue with Gemini 2.5 Pro (Vertex AI): Looping / Incorrect Behavior on Sequential Tool Calls (read_file → write_file) in Single Turn

Model: gemini-2.5-pro-preview-03-25
API/Library: google-genai Python SDK (via Vertex AI client)
Context: Single-stage workflow designed to test sequential tool execution.

Hello Gemini Team and Community,

We’re encountering persistent issues getting gemini-2.5-pro-preview-03-25 (accessed via Vertex AI) to reliably execute a mandatory two-step tool sequence (read_file followed by write_file) within a single turn, despite explicit prompt instructions. The goal is a simple file modification: read a file, modify its content (in memory by the model based on task instructions), and write the modified content back.

We’ve iterated on the system prompt, but the model consistently fails in one of two ways:

  1. Unnecessary Looping: Completes the read/write sequence but then loops back to read_file.
  2. Incorrect Code Generation: Fails to call write_file, instead generating text representing the call.

The Task & Core Prompt Logic:

The system prompt instructs the model to act as an autonomous agent for a single-stage task, with a mandatory sequence:

  1. Call read_file.
  2. Call write_file with modified content.

Critical constraints emphasized that both steps must happen in the single turn, that the write_file tool call must be issued (not represented as text/code), and that this two-step sequence fulfills the entire task.

(See Prompt Variations below for specific wording attempts)

Observed Behavior & Evidence:

Attempt 1: Initial Prompt (Focus on sequence & single turn)

  • Prompt Snippet: Perform both tool executions within this single stage/turn.
  • Behavior: Model correctly calls read_file (Loop 1) and write_file (Loop 2), but then ignores task completion and calls read_file again in Loop 3, getting stuck.
  • Evidence:
    # Loop 2: write_file call issued correctly
    2025-04-19 12:24:54,178 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'tool_call', 'id': 'gemini_func_write_file_1745058294_0', 'name': 'write_file', 'input': {'path': 'app.py', 'content': '# app.py\n...'}}], ...}
    # Loop 2: write_file execution success
    2025-04-19 12:24:54,189 - services.tool_service - INFO - Tool 'write_file' executed successfully.
    # Loop 2: write_file result added to history
    2025-04-19 12:24:54,190 - services.chat_service - DEBUG - CHAT_SERVICE: Loop 2: History *after* adding tool results (len=6): [{'role': 'tool', 'content': [{'type': 'tool_result', 'tool_use_id': 'gemini_func_write_file_1745058294_0', 'content': "Successfully wrote to file 'app.py'.", 'is_error': False}]}]
    # Loop 3: Model calls read_file AGAIN instead of stopping
    2025-04-19 12:25:14,096 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'text', 'text': 'Okay, I will first read the content of `app.py`...'}, {'type': 'tool_call', 'id': 'gemini_func_read_file_1745058314_1', 'name': 'read_file', 'input': {'path': 'app.py'}}], ...}
    # ... continues looping ...
    

Attempt 2: Added Explicit Stop Instructions

  • Prompt Snippet: Task Completion: Once the write_file tool has been called and its result indicates success..., your task for this stage is complete. and After the successful write_file result is processed, you MUST stop processing and end the turn.
  • Behavior: Model called read_file (Loop 1), but in Loop 2, it generated text print(default_api.write_file(...)) containing the correct content instead of issuing the write_file tool call. It stopped with end_turn.
  • Evidence:
    # Loop 1: Calls read_file successfully
    # Loop 2: LLM generates text instead of calling write_file
    2025-04-19 12:19:20,164 - services.llm_providers.google.client - INFO - Finished converting Gemini response. Final status: success, stop_reason: end_turn
    2025-04-19 12:19:20,164 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'end_turn', 'content': [{'type': 'text', 'text': '```tool_code\nprint(default_api.write_file(path=\'app.py\', content=\'# app.py\\n...<modified content>...\'))'}], ...}
    # Chat service loop ends because stop_reason is end_turn
    2025-04-19 12:19:20,167 - services.chat_service - INFO - Chat Service: LLM provided final response or no tools requested. Stop Reason: end_turn. Finishing turn.
    

Attempt 3: Removed Explicit Stop, Reinforced Task Scope

  • Prompt Snippet: Execution Sequence (Mandatory - Complete these two steps ONLY): and Completing the read_file and write_file tool calls fulfills the entire task for this turn. No further tool calls are needed or allowed after the write_file call is issued.
  • Behavior: Reverted to the original looping behavior. It successfully called read_file (Loop 1) and write_file (Loop 2), but then immediately called read_file again in Loop 3 and continued looping.
  • Evidence: (Same pattern as Attempt 1, shown in the latest logs provided)
    # Loop 2: write_file call issued correctly
    2025-04-19 12:24:54,178 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'tool_call', 'id': 'gemini_func_write_file_1745058294_0', ...}]}
    # Loop 2: write_file success result added
    2025-04-19 12:24:54,190 - services.chat_service - DEBUG - CHAT_SERVICE: Loop 2: History *after* adding tool results (len=6): [...]
    # Loop 3: Calls read_file AGAIN
    2025-04-19 12:25:14,096 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'text', 'text': 'Okay, I will first read...'}, {'type': 'tool_call', 'id': 'gemini_func_read_file_1745058314_1', 'name': 'read_file', ...}]}
    # ... continues looping (observed up to Loop 13) ...
    

Comparison:

We tested the exact same workflow structure and initial prompt logic with Anthropic’s claude-3-7-sonnet-20250219 model, which correctly executed the read_filewrite_file sequence and then stopped the turn with end_turn as expected.

Questions:

  1. Is this looping behavior or the failure to call the second tool (generating code instead) a known issue or limitation with gemini-2.5-pro-preview-03-25 when handling strictly sequential tool calls within a single turn, especially when the second call involves a large payload (write_file content)?
  2. Are there recommended prompting strategies or API parameters (beyond standard generation config) specifically for enforcing sequential tool execution and reliable task completion within a single turn for Gemini?
  3. Could the large size of the content argument in the write_file call be contributing to the instability or failure to follow subsequent instructions?

We appreciate any insights or suggestions the community or Google team might have.

Thanks!