Which field do I branch on in a Sume API error: code or next_action?
Branch on the HTTP status, then on error.code. Use retryable, retry_after_seconds and next_action to decide on a resend. Never match on message.

Branch on the HTTP status first, then on error.code. The code is the stable token to switch on: it matches ^[a-z0-9_]+$ and is never a sentence. Use retryable and retry_after_seconds to decide whether resending can succeed, and next_action for what to do about it. The message is for humans and may change, so log it and never match on it.
This follows Format API errors, read 2026-09-29. The envelope fields below are documented for Format run calls; the Errors and rate limits page shows a shorter envelope with code, message, request_id and details for the general API.
What is in a Sume error body?
An error object with code, message, request_id, category, stage, retryable, retry_after_seconds, public_reason, next_action and details. Each one has one job.
| Field | Use it for |
|---|---|
code | The stable token to switch on |
message | A human sentence; log it, never match on it |
request_id | Also sent as the x-sume-request-id header; quote it to support |
retryable, retry_after_seconds | Whether resending the same request can succeed, and how long to wait first |
next_action | authenticate, fix_input, add_funds, retry_later, poll_status, inspect_events or contact_support |
category, stage, public_reason | Coarser labels for dashboards and alerts |
details | Code-specific data such as required_scope or index |
What does a 4xx tell me about retrying?
On a Format run create, a 4xx means nothing ran and nothing was charged, so fix the call rather than retrying it. The docs single out one mistake: retrying a 403 insufficient_scope in a loop is the most common and most expensive mistake. Scopes cannot be added to an existing key, so the fix is to mint a new key.
On that Format run create, a failed create also releases its Idempotency-Key, so once the cause is fixed you can resend with the same key. Generation submits differ: in current code a same-key retry after 429 queue_full or 503 provider_capacity_exceeded replays that refusal, so send a new key once the cause clears.
How do I write the dispatcher?
Map the documented next_action values to your own outcomes, and fall through for anything new. The errors page says to treat the set of codes as open: new codes may appear, so handle the ones you know and fall through on the rest.
def decide(status: int, error: dict) -> str:
if error.get("retryable") is True:
return f"wait {error.get('retry_after_seconds') or 'a backoff'}, then resend"
action = error.get("next_action")
if action == "authenticate":
return "fix the key or its scopes; do not loop"
if action == "fix_input":
return "fix the request body or URL"
if action == "add_funds":
return "top up, then resend"
if action == "contact_support":
return f"quote request_id {error.get('request_id')}"
return "log message and fall through"What are the next_action values on a run receipt?
A different field on a different object: the run receipt carries next_action too, but a Format run emits only three values. It is poll_status while queued or processing, retry_later on a skipped run, and none on every terminal run.
Do failed jobs use the same fields?
A failed generation job exposes public error metadata such as category, stage, retryability, retry-after seconds, public reason and next action. The general errors page maps each job error category to a next step: validation means fix input, auth means check the API key and workspace access, quota means add funds or lower request cost, and queue means retry later (after queue_full or provider_capacity_exceeded, use a new key, as above). Internal provider payloads are never public API fields.
What should I send to support?
The request_id, the run id and the error.code. Do not send API keys, signing secrets or raw media URLs.
Sources
Related posts
More in Developers
- Sume webhook retry schedule: 10 attempts, then what?
Sume tries a webhook up to 10 times. Job webhooks use a fixed 30 s gap; run webhooks back off with jitter up to an hour. What happens next, and how to replay.
- Suno alternative with an API: what Sume's Music Router does
Looking for a music generator you can call from code? What Sume's Music Router takes in, returns and does not do, so you can decide if it fits.
- Talking avatar in JS: make one from Node, play it in React
A talking avatar in JavaScript: create it and send it a script from Node with the Sume SDK, wait for the job, then play the returned MP4 in React.
- Avatar video quality settings: standard, plus or max?
Sume's talking avatar video takes quality standard, plus (default) or max. What each means, the other output fields, and how the preview relates to final tier.
Written by Sume