[MODEL] Haiku 4.5 wrong behaviour tool usage

Resolved 💬 3 comments Opened Oct 21, 2025 by viodid Closed Jan 8, 2026

Preflight Checklist

  • [x] I have searched existing issues for similar behavior reports
  • [x] This report does NOT contain sensitive information (API keys, passwords, etc.)

Type of Behavior Issue

Other unexpected behavior

What You Asked Claude to Do

When operating in an agentic manner with a set of tools, Haiku 4.5 can enter a repetitive loop, calling the same tool for an action it has already completed. This occurs even when the tool provides explicit feedback that the action is redundant and that the model should proceed with a different action.

The model fails to correctly process the tool's output, update its internal plan, and move on to the next logical step. This leads to stalled tasks, wasted computation, and a failure to complete the user's objective.

What Claude Actually Did

We have observed this behaviour in a complex Terraform coding agent. After successfully calling the read_file tool, the agent's state received the following tool result, explicitly telling it to move on:

{
  "tool_call_id": "toolu_vrtx_01RTb5NSYCNJYj5uKEZzKw9z",
  "tool_name": "read_file",
  "result": "The output of this tool has already been provided. Please, use a different tool."
}

Despite this clear instruction in the tool's output, the model's next action was to call read_file on the exact same file again, leading to an infinite loop.

Expected Behavior

The model should follow a logical sequence:

  1. Turn 1: Call read_file with path='config.txt'.
  2. Receive the file content ("INITIAL_CONFIG=true\n").
  3. Turn 2: Process the content and recognize the next step is to write. Call write_file with path='config.txt' and content='INITIAL_CONFIG=true\nSTATUS=updated'.
  4. Receive confirmation that the write was successful.
  5. Turn 3: Call task_complete as the request is fulfilled.

Files Affected

Permission Mode

I don't know / Not sure

Can You Reproduce This?

Yes, every time with the same prompt

Steps to Reproduce

The following Python script provides a minimal, reproducible example that simulates this agentic looping behavior. We define a read_file tool that keeps track of which files have already been read.

It is a minimal example designed to demonstrate the failure pattern. Due to the simplicity of the task and the stochastic nature of LLMs, the model may occasionally succeed and proceed to the write_file step correctly.

from anthropic import Anthropic

client = Anthropic()
MODEL_NAME = "claude-haiku-4-5-20251001"

tools = [
    {
        "name": "read_file",
        "description": "Read the contents of a file at a given path.",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "The path to the file."},
            },
            "required": ["path"],
        },
    },
    {
        "name": "write_file",
        "description": "Write content to a file at a given path. Overwrites the file if it exists.",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "The path to the file."},
                "content": {"type": "string", "description": "The content to write to the file."},
            },
            "required": ["path", "content"],
        },
    },
    {
        "name": "task_complete",
        "description": "Call this function when the user's request has been successfully completed.",
        "input_schema": {
            "type": "object",
            "properties": {
                "summary": {"type": "string", "description": "A brief summary of the work done."},
            },
            "required": ["summary"],
        },
    },
]

messages = [
    {"role": "user", "content": "Please read the file 'config.txt' and then add a new line to it that says 'STATUS=updated'."}
]

print(f"User: {messages[0]['content']}\n")

# Simulate the conversation loop with state tracking
max_turns = 5
files_already_read = set() # Our state tracker

for i in range(max_turns):
    print(f"--- Turn {i+1} ---")
    response = client.messages.create(
        model=MODEL_NAME,
        max_tokens=2048,
        messages=messages,
        tools=tools,
    )

    messages.append({"role": "assistant", "content": response.content})

    if response.stop_reason != "tool_use":
        final_message = response.content[0].text
        print(f"\nModel stopped without tool use: {final_message}")
        break

    tool_calls = [block for block in response.content if block.type == "tool_use"]
    if not tool_calls:
        print("\nModel stopped but no tool calls were found.")
        break

    # Process all tool calls from the model's turn
    for tool_call in tool_calls:
        tool_name = tool_call.name
        tool_input = tool_call.input
        tool_call_id = tool_call.id
        print(f"Model wants to call tool: {tool_name} with input: {tool_input}")

        tool_result_content = ""
        if tool_name == "read_file":
            file_path = tool_input.get("path")
            if file_path in files_already_read:
                # This is the crucial feedback the model ignores
                tool_result_content = f"Error: The content of '{file_path}' has already been provided. Please use a different tool to make progress."
            else:
                tool_result_content = "INITIAL_CONFIG=true\n"
                files_already_read.add(file_path) # Update our state
        elif tool_name == "write_file":
            tool_result_content = f"Successfully wrote to file: {tool_input.get('path')}"
        elif tool_name == "task_complete":
            print(f"\nTask marked as complete. Summary: {tool_input.get('summary')}")
            # End the loop if task is complete
            exit()
        
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_call_id,
                    "content": tool_result_content,
                }
            ]
        })
        print(f"Providing tool result: '{tool_result_content}'")
else:
    print("\nLoop limit reached. The model is likely stuck.")

Claude Model

claude-haiku-4-5-20251001

Relevant Conversation

Impact

Critical - Data loss or corrupted project

Claude Code Version

2.0.24

Platform

Anthropic API

Additional Context

This seems to happen more often with long conversations

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗