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.

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.
| Status and code | Suggested next step in content |
|---|---|
400 invalid_request | Fix the arguments named in details, then call again |
401 unauthorized | Do not retry; the server's key is missing or invalid, tell the user |
402 insufficient_credits | Do not retry; tell the user the balance is too low |
404 not_found | Check the id; it does not exist in this workspace |
429 rate_limited | Wait for retry-after, then repeat the same call |
429 queue_full | Wait for a running job to finish before a new paid submit |
503 provider_capacity_exceeded | Retry 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
- C# HttpClient default timeout: 100 seconds, and how to set it
HttpClient.Timeout defaults to 100 seconds per request and throws TaskCanceledException. How to set it, and why slow API jobs need polling instead.
- Cursor agent subscriptions and scheduled runs calling an API
A Cursor scheduled run can call Sume's HTTP API to start a saved schedule run. The three prerequisites, the request, and why a skipped run still returns 200.
- Cursor Projects subagents and MCP tools: capping Sume spend
Cursor Projects lets a coordinator delegate to subagents. If they share a Sume MCP connection, spend gates sit on each call: idempotency keys, dry runs, caps.
- Cursor self-hosted machines: where a Sume API key can live
Cursor's self-hosted machines run agents on hosts you pick. Sume's key rule is short: trusted servers, CI secret stores or your own machine, never chat.
Written by Sume