10.2 Tool Execution, Multi-Tool Calls & Error Handling

Key Takeaways

  • When Claude invokes tools, the Messages API returns stop_reason: 'tool_use' and the assistant response includes one or more tool_use content blocks containing a unique id, tool name, and pre-parsed input arguments.
  • Multi-tool calling allows Claude to emit multiple parallel tool_use blocks in a single assistant turn; client applications should execute these calls concurrently (e.g., via asyncio.gather or Promise.all) to minimize end-to-end latency.
  • Every tool_use block must be answered with a corresponding tool_result block in the subsequent user turn, strictly matching the tool_use_id and providing string or block-based content.
  • Tool execution failures must be reported back to Claude using is_error: true with descriptive diagnostic feedback, enabling Claude to self-correct its parameters rather than throwing unhandled client-side exceptions that terminate the agent loop.
  • Robust agent loops implement maximum iteration ceilings and cycle-detection guards to prevent infinite self-correction loops when a tool repeatedly returns errors.
Last updated: September 2026

Tool Execution, Multi-Tool Calls & Error Handling

Exam Blueprint Focus: Building production agents requires handling the complete execution lifecycle of tool calling. You must master response detection via stop_reason: "tool_use", extract and safely parse tool_use content blocks, implement asynchronous multi-tool execution for parallel tool invocations, construct valid tool_result messages adhering to the strict 1:1 pairing rule, and leverage is_error: true to drive autonomous model self-correction.


Detecting and Parsing Tool Call Responses

When Claude determines that an external action or data lookup is required to satisfy a user prompt, it halts text generation and returns an assistant message containing structured tool invocation data. The client application must inspect two critical fields in the response:

  1. stop_reason: When tools are invoked, stop_reason is set to "tool_use" (in contrast to normal prose completions where stop_reason is "end_turn", or length truncation where it is "max_tokens").
  2. content Blocks: The assistant message content array contains one or more content blocks. Claude may output a text block containing its chain-of-thought explanation immediately followed by one or more tool_use blocks.

Anatomy of the tool_use Block

Each tool_use block possesses a strict anatomical structure:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I will look up the current weather conditions for Tokyo to answer your question."
    },
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835l192",
      "name": "get_weather",
      "input": {
        "city": "Tokyo",
        "units": "metric"
      }
    }
  ]
}
  • type (string): Always "tool_use".
  • id (string): A unique, globally distinct identifier generated by Anthropic, prefixed with toolu_. This ID is the primary key that links this specific request to its eventual execution result.
  • name (string): Matches the tool name defined in the request's tools array.
  • input (object): A pre-parsed JSON object containing the parameters generated by Claude conforming to your declared input_schema.

Safe Parsing Best Practices

In official Anthropic SDKs (Python and TypeScript), the input payload is already deserialized into native dictionaries or objects. However, production applications should never blindly pass this dictionary directly into database queries or shell commands. Always validate parameters using runtime validation frameworks like Pydantic in Python or Zod in TypeScript:

from pydantic import BaseModel, Field, ValidationError

class WeatherInput(BaseModel):
    city: str = Field(min_length=1, max_length=100)
    units: str = Field(pattern="^(metric|imperial)$")

try:
    # tool_block.input is a dict
    validated_args = WeatherInput(**tool_block.input)
    execute_weather_lookup(validated_args.city, validated_args.units)
except ValidationError as e:
    # Handle schema validation mismatch
    report_tool_error(tool_block.id, str(e))

Multi-Tool Calling: Parallel Tool Invocation

Modern Claude models (Claude Sonnet 5, Claude Opus 5) feature native multi-tool calling. When a user's request involves multiple independent data points, Claude does not issue sequential single-tool calls across multiple turns. Instead, Claude emits multiple tool_use content blocks within a single assistant message:

{
  "role": "assistant",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01_tokyo_weather",
      "name": "get_weather",
      "input": {"city": "Tokyo"}
    },
    {
      "type": "tool_use",
      "id": "toolu_02_london_weather",
      "name": "get_weather",
      "input": {"city": "London"}
    },
    {
      "type": "tool_use",
      "id": "toolu_03_paris_weather",
      "name": "get_weather",
      "input": {"city": "Paris"}
    }
  ]
}

Concurrency and Latency Optimization

Executing multi-tool calls serially introduces severe latency. If fetching weather for one city takes 400ms, running three calls sequentially incurs 1,200ms of client execution latency before Claude can even begin synthesizing the final answer. Client applications should dispatch parallel tool calls concurrently using asynchronous concurrency primitives such as asyncio.gather in Python or Promise.all in TypeScript:

import asyncio

async def dispatch_tool_call(tool_use_block):
    tool_id = tool_use_block.id
    name = tool_use_block.name
    args = tool_use_block.input
    
    try:
        result_data = await registry.execute(name, args)
        return {
            "type": "tool_result",
            "tool_use_id": tool_id,
            "content": json.dumps(result_data)
        }
    except Exception as exc:
        return {
            "type": "tool_result",
            "tool_use_id": tool_id,
            "content": f"Error executing {name}: {str(exc)}",
            "is_error": True
        }

# Execute all tool calls emitted in the turn concurrently
results = await asyncio.gather(*[
    dispatch_tool_call(block)
    for block in response.content
    if block.type == "tool_use"
])

Formatting tool_result Content Blocks

Once the client application executes the requested tools, it must format the execution outputs and submit them back to the Messages API in a new user turn. This message must follow strict structural rules:

  1. role (string): Must be "user".
  2. content (array): An array of tool_result content blocks.

Anatomy of a tool_result Block

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835l192",
      "content": "{\"temperature_celsius\": 22.4, \"condition\": \"Partly Cloudy\", \"humidity\": 65}",
      "is_error": false
    }
  ]
}
  • type (string): Always "tool_result".
  • tool_use_id (string): Must match the exact id from the corresponding tool_use block.
  • content (string or array): The output data returned by your function. Typically a JSON string or plaintext message. It can also be an array of nested blocks (e.g., returning base64 images via {"type": "image", ...}).
  • is_error (boolean, optional): Set to true if the tool encountered an operational error, business logic failure, or runtime exception.

The Strict 1:1 Invariant

The Anthropic Messages API enforces a strict 1:1 pairing invariant:

  • Every tool_use block emitted by Claude MUST receive a corresponding tool_result block in the immediately following user message.
  • If Claude emits three tool_use blocks, the subsequent user message must contain exactly three tool_result blocks matching those three IDs.
  • If any tool_use_id is missing, duplicated, or does not match an antecedent tool_use block, the Messages API rejects the request with an HTTP 400 invalid_request_error.

Preserving Message History Integrity

When appending tool results, you must maintain proper conversation turn alternation. The assistant message containing the tool_use blocks must be appended to the history first, followed immediately by the user message containing the tool_result blocks. Dropping or altering the assistant turn invalidates the conversation sequence:

[User Message: "What's the weather in Tokyo?"]
                     |
                     v
[Assistant Message: tool_use (id: 'toolu_123', name: 'get_weather')]  <-- MUST BE PRESERVED
                     |
                     v
[User Message: tool_result (tool_use_id: 'toolu_123', content: '...')] <-- IMMEDIATE FOLLOW-UP
                     |
                     v
[Assistant Message: "The weather in Tokyo is currently..."]

Self-Correction Patterns via is_error: true

A critical distinction tested on the CCDV-F exam is the difference between client-side exceptions and model-assisted self-correction:

  • Brittle Anti-Pattern (Unhandled Client Exception): The client catches an error from a database or API, raises an unhandled exception in the application runtime, and crashes the agent loop. The conversation aborts, frustrating the user.
  • Resilient Architectural Pattern (Model Self-Correction): The client catches the error, marks the tool_result block with is_error: true, and provides a detailed, actionable diagnostic message. When Claude receives is_error: true, its internal reasoning kicks in: it evaluates what parameter was incorrect, adjusts its inputs, and re-invokes the tool with corrected parameters.
// Client catches a SQL syntax error and returns is_error: true
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_sql_query_4982",
      "content": "Database Execution Error: Column 'user_full_name' does not exist in table 'customers'. Available columns are: ['customer_id', 'first_name', 'last_name', 'email', 'created_at'].",
      "is_error": true
    }
  ]
}

Upon receiving this error payload, Claude recognizes that user_full_name was invalid, inspects the list of available columns, and generates a corrected query: SELECT first_name, last_name FROM customers WHERE ....

Authoring Actionable Error Payloads

To maximize Claude's self-correction fidelity, follow these error reporting guidelines:

  1. Provide Specific Root Causes: Avoid generic messages like "Error: 500 Internal Server Error". Instead, state: "Error 404: Resource not found. No invoice exists with ID 'INV-9021'."
  2. Include Valid Ranges and Expected Formats: If a date parsing error occurs, return: "Invalid date format: '10/24/2026'. Dates must follow ISO 8601 YYYY-MM-DD format (e.g., '2026-10-24')."
  3. Do Not Hide Schema Constraints: If an integer parameter was out of bounds, return the valid range: "Value 500 exceeds maximum allowable batch_size of 100."

Loop Ceilings & Cycle Prevention

Autonomous self-correction introduces the risk of infinite loops if a tool persistently fails. Production agent loops must implement strict safety guards:

  • Maximum Retries: Cap consecutive tool error retries (typically max_tool_retries = 3).
  • Parameter Hash Cycle Detection: Track previously attempted argument payloads; if Claude submits identical arguments that previously generated an error, abort the loop and inform the user.
Loading diagram...
Multi-Tool Execution, Error Self-Correction, and Recovery Lifecycle
Test Your Knowledge

When processing a response from the Anthropic Messages API, an application detects stop_reason: 'tool_use' and identifies two tool_use content blocks in response.content. What is the required structure and sequencing of the subsequent request to the Messages API?

A
B
C
D
Test Your Knowledge

A developer implements an autonomous database querying agent using Claude Sonnet 5. When Claude generates a SQL query that triggers a database syntax error ('PG::SyntaxError: syntax error at or near FROMM'), how should the application handle this exception to achieve automated self-correction?

A
B
C
D
Test Your Knowledge

An AI service invokes three independent API tools concurrently using asyncio.gather in response to a multi-tool call from Claude. Two tools execute successfully within 150ms, but the third tool encounters an HTTP 504 Gateway Timeout. What is the correct way to construct the follow-up message to Claude?

A
B
C
D