Structured Error Responses

How a tool reports failure decides whether an agent recovers intelligently or fails blindly. Learn the error categories and the empty-result trap.

Lesson 9 of 3030% of the guide
Prefer to learn by doing?

When a tool fails, the shape of the error decides whether the agent retries sensibly, self-corrects, escalates, or gives up. The MCP protocol carries an `isError` / `is_error` flag so Claude treats a failure as a failure instead of parsing error text as a successful result. Beyond that flag, structured metadata lets the model reason about the *right* recovery path.

Four error categories

Every failure falls into one of four categories, and each implies a different recovery strategy. The category plus an isRetryable boolean is what lets Claude choose correctly.

CategoryExampleisRetryableRecovery
transientTimeout, rate limit, service downtrueRetry the same call after a short delay
validationBad format, missing field, out of rangetrueFix the input, then retry
businessPolicy violation, limit exceededfalseDo not retry; take an alternative workflow
permissionAccess denied, missing credentialsfalseEscalate or use different credentials
{
  "isError": true,
  "errorCategory": "business",
  "isRetryable": false,
  "description": "Refund of $850 exceeds the $500 automatic refund ceiling. The same policy applies on every attempt. Escalate to a human agent with the refund details."
}
Reporting a failure
Don't

Returning a raw stack trace or a generic `"Operation failed"` strips Claude of the information it needs — no category, no retry signal, no recovery move.

{ "error": "Error: 500 Internal Server Error\n  at db.query (db.js:42)" }
Do

A structured error carries errorCategory, isRetryable, and a description that names the next move, so the agent recovers instead of failing blindly.

{
  "isError": true,
  "errorCategory": "business",
  "isRetryable": false,
  "description": "Refund exceeds the $500 ceiling. Escalate to a human agent."
}
isRetryable means feasibility, not sameness

isRetryable answers 'is there any path to success through retrying?' — not 'will the identical call work?'. Transient errors retry unchanged; validation errors retry after correction; business and permission errors are not retryable at all.

Access failure vs. valid empty result

This distinction is tested directly. An access failure means the tool could not reach the data source (timeout, auth failure, downtime) — the data might exist but was never queried, so isError: true. A valid empty result means the query ran correctly and simply found nothing — isError: false with resultCount: 0. Collapse these two into one and agents retry successful empty queries forever, burning effort before wrongly escalating a database that is working fine.

// Valid empty result — do NOT retry
{
  "isError": false,
  "resultCount": 0,
  "description": "No customer matched 'john@example.com'. The query executed successfully but returned no rows."
}

// Access failure — retryable
{
  "isError": true,
  "errorCategory": "transient",
  "isRetryable": true,
  "description": "Connection to the customer database timed out after 5s. The query did not execute."
}
Access failure vs. empty result
Don't

Collapsing both into one shape — reporting a query that ran and matched nothing as an error (or a timeout as an empty success) makes the agent retry a working database forever, then wrongly escalate.

// Query ran, found nothing — but flagged as an error
{ "isError": true, "description": "No results." }
Do

Keep them distinct. A valid empty result is isError: false with resultCount: 0; an access failure that never queried the source is isError: true and retryable.

// Valid empty result — accept, do NOT retry
{ "isError": false, "resultCount": 0 }
Retrying an empty success

No matching data is a correct answer, not a failure. If a query runs and returns zero rows, an identical retry returns zero rows again. The agent should accept the empty result and respond, never loop.

Error propagation in multi-agent systems

  • Recover locally first — a search subagent retries a timed-out search before escalating.
  • Propagate only unresolvable errors — after exhausting retries, report upward with context.
  • Include partial results — 'searched 3 of 5 sources; 4 and 5 timed out; here are the 3 that worked.'
  • Never suppress silently — returning empty as success hides failure from the coordinator.
Make errors self-describing

Every structured error should carry errorCategory, isRetryable, and a human-readable description that names the recovery move. Generic 'Operation failed' text strips Claude of the information it needs to recover.

How the exam will try to trick you

The distractors below look right under time pressure — learn the tell.

  1. The trap

    A lookup returns an empty array; the agent retries 3 times, then escalates to a human.

    Correct answer

    Treat a valid empty result (isError: false, resultCount: 0) as a final answer — accept it and respond.

    Why: A query that ran and matched nothing returns the same empty result on every retry; no data exists to find.

  2. The trap

    Return a generic "Operation failed" message (or a raw stack trace) when a tool fails.

    Correct answer

    Return structured metadataerrorCategory, isRetryable, and a description naming the next move.

    Why: Without the structured fields the agent cannot tell a transient failure from a business violation.

  3. The trap

    Treat a business error (policy violation, limit exceeded) as retryable and try again.

    Correct answer

    Set isRetryable: false and take an alternative path such as escalation.

    Why: The same policy constraint applies on every attempt, so retrying can never succeed.

  4. The trap

    Have a subagent return empty results as success when its search actually failed.

    Correct answer

    Propagate the failure upward with context after local retries are exhausted.

    Why: Silently suppressing errors hides them from the coordinator, which then cannot recover.

Key takeaways

  • Use the is_error / isError flag so Claude treats failures as failures, not as valid output.
  • Classify errors as transient, validation, business, or permission — each maps to a distinct recovery.
  • isRetryable signals whether any retry path exists, not whether the identical call will work.
  • Access failure (isError: true) and valid empty result (isError: false, count 0) must be distinct.
  • Business and permission errors are never retryable; retrying only wastes effort.
  • In multi-agent systems, recover locally, propagate unresolvable errors with partial context.

Frequently asked questions

What is the is_error flag in Claude tool use?+

It is a boolean on a tool result that tells Claude the tool call failed rather than succeeded. Without it, Claude may parse an error message as if it were a valid result. Setting is_error true lets the model reason about recovery instead of treating the failure text as data.

How should a tool report that it found no data?+

As a successful call with isError false and resultCount 0, plus a description saying the query ran but matched nothing. This is different from an access failure, which sets isError true. Confusing the two makes agents retry empty results endlessly.

When should an agent retry a failed tool call?+

Only for retryable categories: transient errors retry unchanged after a delay, and validation errors retry after fixing the input. Business and permission errors set isRetryable false, so the agent should escalate or take an alternative path instead of retrying.

Practice makes pass

Ready to test what you just learned?

Reading gets you familiar — answering questions gets you certified. Jump into free practice or sit a full timed mock exam, scored 100–1000 just like the real thing.

No sign-up required · Explanation for every answer · Works offline