Claude tool_result is_error: return Sume API errors

When a Sume REST call fails inside your Claude tool loop, send the error back as a tool_result with is_error true and say what to try next. Code and table.

5 min readSume
All posts

If your tool for Claude calls the Sume API and Sume returns an error, send it back as a tool_result block with is_error set to true. Put in the content what went wrong and what Claude should try next. Claude then works the error into its reply or adjusts the next call, instead of guessing from an empty result.

That is from Anthropic's handle tool calls page, read 2026-09-29. The error fields and codes are from Sume's errors and rate limits page. Your API key stays on your server; Claude only sees the text you return.

What does the tool_result look like?

Anthropic's example is a user message with a tool_result block that carries the tool_use_id, the error text as content, and "is_error": true. The page's advice is to avoid a bare "failed" and include the cause and the next step, such as a retry delay.

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "rate_limited (429): too many requests. Wait for the retry-after value, then repeat the same call with the same idempotency key. request_id req_123",
      "is_error": true
    }
  ]
}

What does a Sume error contain?

Sume's error body has an error object with code, message, request_id and details. Build your content string from those fields rather than forwarding the raw body. Keep the request_id, since Sume's page says it is safe to share with support and should go in any issue report.

async function toToolResult(toolUseId: string, res: Response) {
  const body = await res.json().catch(() => ({}));
  const e = body.error ?? {};
  const retryAfter = res.headers.get("retry-after");
  const text = [
    `${e.code ?? "unknown"} (${res.status}): ${e.message ?? "no message"}`,
    retryAfter ? `retry-after ${retryAfter}s.` : "",
    e.request_id ? `request_id ${e.request_id}` : "",
  ]
    .filter(Boolean)
    .join(" ");
  return {
    type: "tool_result" as const,
    tool_use_id: toolUseId,
    content: text,
    is_error: true,
  };
}

What should each error tell Claude to do?

Map each common Sume error to one instruction Claude can act on.

Codes from Sume's errors page, read 2026-09-29; the advice column is a suggested wording, not Sume's text.
Status and codeSuggested next step in content
400 invalid_requestFix the arguments named in details, then call again
401 unauthorizedDo not retry; the server's key is missing or invalid, tell the user
402 insufficient_creditsDo not retry; tell the user the balance is too low
404 not_foundCheck the id; it does not exist in this workspace
429 rate_limitedWait for retry-after, then repeat the same call
429 queue_fullWait for a running job to finish before a new paid submit
503 provider_capacity_exceededRetry later with the same idempotency key

Why keep the same idempotency key on a retry?

Sume's page says not to retry unsafe submit requests without an Idempotency-Key, and says to retry a full provider queue with the same key. In your error text, tell Claude to reuse the key from the first attempt, so a retried paid create does not become a second job. For the polling side, Sume's jobs docs cover the wait and result calls. See also how long to wait after a 429.

When is a tool_result not an error?

Anthropic's page says is_error is for a tool that failed to run. A job that finished with a failed status is a successful read of a failed job, and Sume's job error metadata (category, stage, retryability, public reason, next action) is worth passing through as ordinary content. Reserve is_error for the call itself failing.

Server tools are different: the page says you do not handle is_error for them, because Claude handles those errors itself.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume