Failed Sume video job: resubmit or stop? Read retryable, next_action
Sora said failed and little else. A failed Sume job carries an error with category, retryable, retry_after_seconds and next_action. A small decision function.

Resubmit a failed Sume video job only when its error says retryable is true, and wait retry_after_seconds when that field is set; for next_action fix_input, change the request instead, and for add_funds, top up first. The error object on the job, described as PublicDeveloperApiJobError in the OpenAPI document, carries category, stage, retryable, retry_after_seconds, public_reason and next_action for this purpose.
OpenAI's video guide, read 2026-10-08, lists failed as a terminal status; I found no field there that tells you whether to try again.
Fields and the action they imply
Read the error from GET /v1/jobs/{id}/status, under the job object, or from the terminal event in the events timeline.
| Field | Values or type | What to do |
|---|---|---|
| retryable | boolean | false means retrying the same request is not expected to help |
| retry_after_seconds | integer or null | wait this long, null means no specific delay |
| next_action | fix_input, add_funds, retry_later, inspect_events, contact_support, and others | branch on it |
| category | validation, auth, quota, queue, generation_rejected, generation_timeout, and others | group for dashboards |
| public_reason | string such as generation_rejected | stable key for support tooling |
A decision function
This runs as written; the sample is the example that appears in the OpenAPI document. Replace it with the error you read from a real job.
def decide(error):
if error is None:
return "no error on this job"
action = error["next_action"]
if error["retryable"] and action == "retry_later":
wait = error.get("retry_after_seconds") or 60
return "resubmit with a new key after %d s" % wait
if action == "add_funds":
return "top up the balance, then resubmit with a new key"
if action == "fix_input":
return "change the request; the same body will fail again"
return "log the job events and send to a human"
def main():
sample = {"code": "generation_failed", "message": "Generation failed.",
"category": "generation_rejected", "stage": "generation_processing",
"retryable": False, "retry_after_seconds": None,
"public_reason": "generation_rejected", "next_action": "inspect_events"}
print(decide(sample))
main()Why a new key
The docs say the same Idempotency-Key and body replays the first job, so assume a resubmit under the old key hands you the failed job back. Derive the new key from your record id plus an attempt number, and cap attempts at two or three so a persistent failure does not keep reserving credits.
For failures that come back as HTTP errors instead of failed jobs, see the error mapping post; for a prompt the model refused, see the rejection post.
Sources
Related posts
More in Developers
- FastAPI 0.142 native OpenTelemetry: tag spans with a Sume job id
FastAPI 0.142.0 added native OpenTelemetry. Put the Sume job_id on the current span in a webhook route so a failed render shows up next to the HTTP trace.
- FastAPI background poll of a Sume job: use next_poll_after_seconds
Poll a Sume job from an asyncio task in a FastAPI 0.142 app, wait for the server-provided interval, stop at terminal, and never resubmit after a timeout.
- Fastify: verify a Sume video webhook where Sora's video.completed was
Swap the Sora video.completed handler for Sume's job.completed in Fastify. Keep the raw string body, verify sume-v1 with the SDK, and refuse an empty secret.
- Find the timestamp of a quote in a recording with Sume STT words
Sume's STT result returns every word with start and end seconds. A short Python function finds a quoted phrase and returns where to cut. About a cent a minute.
Written by Sume