output_extraction_failed: harvest_unavailable or harvest_threw?
output_extraction_failed has two reasons with opposite handling: reread a completed run, or report a host defect. Here is how to tell them apart.

Two reasons, two actions
output_extraction_failed on a Format run means the projection onto your output_schema could not run. The error's details.reason decides what you do. harvest_unavailable means the media was not readable while the run finalized. harvest_threw means the host's harvest crashed after the run finished.
| details.reason | Run status | What happened | What to do |
|---|---|---|---|
| harvest_unavailable | completed | Media was not readable at finalize; the receipt fills in on the next read | Read the run once more, then retry with a new key |
| harvest_threw | failed | The host harvest crashed; details.thrown holds the frame and build | Report the run id; the clips are real and a retry does not regenerate them |
Harvest unavailable: read again
Here the run stays completed. Do not treat the first read as final. Fetch GET /v1/format-runs/{run_id} again, or result_url, after a short wait. The docs say the receipt fills in on the next read.
A webhook consumer can see this as a normal status: "OK" event whose payload has output: null. Branch on outcome: degraded means real artifacts with no structured output. That is the signal to reread or review by hand, not to bill the customer as delivered.
Harvest threw: it is a host defect
When harvest_threw appears the run is failed, and details.thrown holds the frame that threw and the build. That is information for Sume, not a problem with your request. Report the run id and request_id.
Your clips are not lost. The artifacts on the thread are real, and a retry does not generate them again. If the ledger of the run holds none of the Format's declared media, you will see deliverable_missing instead of this code.
A handler that covers both
Keep the branch small and keyed on documented fields. Use error.code first, then details.reason. For completed runs with a null output, read output_error before you read output.
Treat the set of codes as open. New ones can appear, so your handler needs a fallback for codes it does not know.
harvest_unavailable: reread, then retry once with a newIdempotency-Key.harvest_threw: stop, report, keepartifacts[]URLs.- Score a run as delivered only after you have read
output_errorand checkedprimary_output_url.
Telling it apart from the schema failures
Do not mix this code up with output_schema_unsatisfied. That one means the run finished but its result did not match your schema, or referenced media the run did not produce. The details list rejected_urls[] or violations[] with a harvested count by media type, and the usual cause is a schema that asks for a file the Format never makes. Extraction failures are about the host's ability to read the run, not about the shape you asked for.
A useful habit is to read filled_by on a successful output. agent means the run submitted your object itself. projection means a constrained pass built it afterwards from the run's media and closing text. A run that sits on the projection path is the one where extraction problems matter most, because the projection has only those two facts to work from.
None of this changes billing for finished generation. You pay for media that completed before a failure, and usage on the receipt shows the amount.
Sources
Related posts
More in Formats
- primary_output_missing: your schema passed but the key is empty
A Format run can satisfy your output_schema and still fail because primary_output_key is empty. Here is how to read it and fill the gap.
- provider_credits_exhausted: why a Format run says retryable false
This Format run error is on Sume's side, not your balance or input. details.retryable is false, so do not re-fire in a loop. Here is the handling.
- Format run usage after a cancel: debited, held, refunded and final
After you cancel a Sume Format run, usage shows the spend. How debited, held, refunded and final differ, and when the number is settled in a holiday batch.
- Spend cap 0 is a 400 and null is $500: set a cap per holiday item
Sume's generation_spend_cap_usd rejects 0 and anything above 500, and null means the $500 maximum. What to send on each item of a holiday bulk queue.
Written by Sume