Retry failed items in an AI video batch: Sume bulk-run recovery
When a Sume bulk queue completes, read counts.failed, then continue each failed child that left work behind or resubmit it under a new Idempotency-Key.

To retry failed items in a Sume bulk batch, wait until the queue's status is completed, read counts.failed and counts.canceled, and handle each failed item by type: continue a child run that left work behind with previous_run_id, and resubmit the rest as new runs under a new Idempotency-Key.
The steps below come from Sume's Bulk runs, Runs and results, and Errors and spend docs pages, read on 2026-09-26. How a queue is created and drained is covered in Sume Format bulk runs.
How do I find the failed items in a batch?
Poll GET /v1/format-run-queues/{queue_id} until the queue status is completed. That means every item is terminal, not that all succeeded, and finished_at is set. Then branch on counts.failed and counts.canceled.
Each row in items carries index, status, run_id, and error. index is the zero-based position you submitted, so keep your own map from source row to index. This filter lists the rows to recover:
curl -sS "https://api.sume.com/v1/format-run-queues/$QUEUE_ID" \
-H "Authorization: Bearer $SUME_API_KEY" \
| jq -c '.data.items[] | select(.status == "failed" or .status == "canceled")
| {index, status, run_id, code: .error.code}'Which kind of failure is each item?
An item's status and run_id place it in one of three cases. When a child run settles, the item error is generic, so read the real reason on the child's own receipt at GET /v1/format-runs/{run_id}.
| Item | What happened | Next step |
|---|---|---|
failed, run_id: null | The child never started. error holds the create-run failure from that attempt, for example format_run_failed_to_start. Wallet and admission failures after the 202 land here. | Fix what error.code names, then resubmit the row as a new run. |
failed, run_id set | The child run failed, or was skipped. A failed child gives the item format_run_failed. | Read the child's receipt, then continue it or start it again. |
canceled | The child run was stopped with POST /v1/format-runs/{run_id}/cancel. The item gets format_run_canceled. | Resubmit only if you still want that row. |
Should I continue a failed child or start it again?
Continue it when the failure left clips behind. A run is continuable when its receipt shows a non-null thread_id and either a completed status or a non-empty artifacts[]. Send its id as previous_run_id on a new run of the same Format, never a thread id, and bind the same output_schema again: it is per run, not inherited. A run that left nothing is refused with 400 previous_run_not_resumable, so start that row fresh.
The child's error.code narrows the choice. The set is open, so fall through on codes you do not know:
incomplete_assembly: continue withprevious_run_id. The finished clips are on the thread and are not regenerated.primary_output_missing: continue the run to fill the gap, or retry.unattended_blocked: fix the input or the brief, then retry with a newIdempotency-Key.output_schema_unsatisfied: usually a schema demanding a file the Format never makes. Loosen it to nullable or change the instruction first.provider_unavailableormcp_unavailable: retry with a newIdempotency-Key.format_run_failed: the generic code. Compareusage.billable_amount_usd_microswithusage.generation_spend_cap_usd_micros, because a run that wanted to spend past its cap lands here.
How do I resubmit only the failed rows?
Build a new bulk request from the failed rows only. Each item is the same body as a single POST …/runs, so a continuable row goes in with previous_run_id, a fresh row goes in with its original instruction and input, and each can carry its own generation_spend_cap_usd.
Send a new Idempotency-Key. The first batch's key with the same { concurrency, items } returns 202 and the old queue; with a different payload it is 409 idempotency_conflict. For a few rows, single runs work too. Derive each key from the row plus a version you bump for a deliberate re-run: the same key with the same body returns the original receipt with idempotency_hit: true, not a second run.
curl -sS -X POST "https://api.sume.com/v1/formats/acme/product-promo/bulk-runs" \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spring-catalog-batch-1-retry-1" \
-d '{
"concurrency": 2,
"items": [
{ "previous_run_id": "arun_…", "instruction": "Redo the failed part only. Keep the rest unchanged." },
{ "instruction": "clip 4", "input": { "url": "https://example.com/4.jpg" } }
]
}'What does a retry cost?
A continuation is a new run: new id, new receipt, its own spend cap, and its own single webhook. The agent is replayed what it produced, so it can redo one part and leave the rest alone. usage stays per run.
Generation that finished before a failure is billed, and a later step failing does not refund it. A 4xx at create and an idempotent 200 replay cost nothing. For mcp_unavailable, no generation ran and nothing was billed.
What does the bulk API not do for retries?
A failed or canceled item is terminal: it frees its slot and the rest of the queue continues, so recovery is your code's job. The queue's general limits, including no queue webhook and no list-queues or cancel-queue endpoint, are covered in Sume Format bulk runs. Three rules shape a retry:
- A retry item needs its own
communication.webhook_urlif you want a callback. A canceled or skipped run never delivers one. - A continuation never changes the original run, so point your row at the new run id.
- Every retried child still passes wallet, workspace generation concurrency, and spend-cap admission. After
insufficient_credits, top up first: retrying without doing so returns the same answer.
Sources
Related posts
Written by Sume