tool_use_failed

Tool Use invalid_request_error Schema Error

Claude returned a tool_use block, but your code sent back a malformed tool_result. The fix is almost always a schema mismatch — wrong content structure, missing tool_use_id, or wrong message role.

The complete tool use flow

Understanding the full cycle prevents most errors:

# Step 1: Send a request with tools defined
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=[{
        "name": "get_weather",
        "description": "Get weather for a city",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"}
            },
            "required": ["city"]
        }
    }],
    messages=[{"role": "user", "content": "What's the weather in Paris?"}]
)

# Step 2: Check if Claude wants to use a tool
if response.stop_reason == "tool_use":
    tool_use_block = next(b for b in response.content if b.type == "tool_use")
    tool_name = tool_use_block.name           # "get_weather"
    tool_input = tool_use_block.input         # {"city": "Paris"}
    tool_use_id = tool_use_block.id           # "toolu_01XYZ..."

    # Step 3: Run the actual tool
    result = get_weather(tool_input["city"])   # your function

    # Step 4: Send result back (CRITICAL: correct format!)
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=[...],  # same tools
        messages=[
            {"role": "user", "content": "What's the weather in Paris?"},
            {"role": "assistant", "content": response.content},   # full content list!
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_use_id,   # must match!
                    "content": str(result)         # string or content array
                }]
            }
        ]
    )

Correct tool_result schema

# Minimal correct tool_result (string content)
{
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01XYZ...",   # from Claude's tool_use block
            "content": "The weather in Paris is 22°C and sunny."
        }
    ]
}

# With structured content (for multiple blocks)
{
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toolu_01XYZ...",
            "content": [
                {"type": "text", "text": "Result: 42"},
                {"type": "text", "text": "Additional info here"}
            ]
        }
    ]
}

# Reporting a tool error to Claude
{
    "role": "user",
    "content": [
        {
            "type": "tool_result",
            "tool_use_id": "toulu_01XYZ...",
            "is_error": true,
            "content": "Error: city 'Xyz' not found in weather database"
        }
    ]
}

Common mistakes

1. Putting tool_result in assistant role (wrong!)

# WRONG — tool_result must be in a user message
{"role": "assistant", "content": [{"type": "tool_result", ...}]}

# CORRECT
{"role": "user", "content": [{"type": "tool_result", ...}]}

2. Using string content instead of list

# WRONG — content must be a list when it contains a tool_result
{"role": "user", "content": "The temperature is 22C"}

# CORRECT
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "...", "content": "22C"}]}

3. Sending only the tool_result turn (missing history)

# WRONG — Claude's tool_use turn must be in messages history
messages=[
    {"role": "user", "content": "What's the weather?"},
    # missing: assistant's response with tool_use block!
    {"role": "user", "content": [{"type": "tool_result", ...}]}
]

# CORRECT — include the assistant's full response.content
messages=[
    {"role": "user", "content": "What's the weather?"},
    {"role": "assistant", "content": response.content},  # the full content list
    {"role": "user", "content": [{"type": "tool_result", ...}]}
]

4. Mismatched tool_use_id

# Claude returns: {"type": "tool_use", "id": "toolu_01AbC...", ...}
# You must echo THAT exact id back:
{"type": "tool_result", "tool_use_id": "toolu_01AbC...", ...}  # exact match

TypeScript implementation

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

async function runToolLoop(userMessage: string) {
  const tools: Anthropic.Tool[] = [{
    name: "get_weather",
    description: "Get current weather for a city",
    input_schema: {
      type: "object" as const,
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  }];

  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMessage }
  ];

  while (true) {
    const response = await client.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 1024,
      tools,
      messages,
    });

    // Add assistant's full response to history
    messages.push({ role: "assistant", content: response.content });

    if (response.stop_reason !== "tool_use") break;

    // Collect all tool results
    const toolResults: Anthropic.ToolResultBlockParam[] = [];
    for (const block of response.content) {
      if (block.type === "tool_use") {
        const result = await dispatchTool(block.name, block.input as any);
        toolResults.push({
          type: "tool_result",
          tool_use_id: block.id,
          content: String(result),
        });
      }
    }

    messages.push({ role: "user", content: toolResults });
  }

  return response;
}

FAQ

Can I have multiple tool uses in one response?
Yes — Claude may call multiple tools in a single response. You must return a tool_result for every tool_use block before Claude can continue. Collect all results in one user message.
What happens if my tool throws an exception?
Return the error to Claude using "is_error": true in the tool_result. Claude will acknowledge the failure and may suggest alternatives. Never crash your agentic loop on a tool error.
Can tool_result content contain images?
Yes — use the content array form and include image blocks: [{"type": "image", "source": {...}}, {"type": "text", "text": "description"}].