A Sume 400 that returns a job id: input rejected at provider submit
Most Sume 400s create nothing, but a provider input rejected at submit leaves a failed job, a request_id and a refund. How to read it and what key to use.

A Sume 400 normally means nothing was created. There is one exception: when the request passes admission but the generation input is refused at the moment Sume submits it, the response is still 400 invalid_request, yet error.details carries a request_id, a status_url and a job. A failed job exists, its hold is refunded, and the fix is to change the input and send it under a new Idempotency-Key.
Why does a 400 have a job id?
Paid generation reserves the balance and writes a job row before it calls the generation runtime. If the runtime layer then rejects the input shape, for example a missing image_url, audio_url or video_url for a mode that needs one, or an unsupported generation model, the API marks the job failed with the internal code invalid_provider_input, refunds the usage with the reason provider_submit_failed, and answers 400 with the specific message, such as Missing image_url.
The docs say the same in general terms: the reservation is taken when Sume accepts the request, and failed jobs release or refund it where applicable. The body also carries result_url and the public job, so a client that logs only request_id can find the row later in the dashboard Jobs tab.
What does the job say when I poll it?
The public job error is deliberately more generic than the 400 message. For invalid_provider_input the job reports the message Invalid generation input., category: validation, stage: validation, retryable: false, public_reason: invalid_input and next_action: fix_input. If you need the specific sentence, keep the one from the 400 body.
Do not treat the failed job as something to wait on. It is terminal, and GET /v1/jobs/{id}/result on a failed job answers 409 job_not_completed-class conflicts rather than a result, so read the job record instead.
| Where you look | What you see |
|---|---|
| HTTP response | 400 invalid_request, specific message, details.request_id, details.status_url, details.job |
| Job error code (internal) | invalid_provider_input |
Job public_reason | invalid_input |
Job retryable / next_action | false / fix_input |
| Balance | hold refunded with reason provider_submit_failed |
Which Idempotency-Key do I send after fixing it?
Use a new one. The same key with a different body is 409 idempotency_conflict by the documented rule, and on the routes that read the stored job back on a repeat, the same key with the same body re-raises the stored failure instead of trying again. A corrected request is a different operation, so give it its own key.
A short rule for a client: on invalid_request, look for details.request_id. If it is present, log it, show the user the message, and do not retry. If it is absent, nothing exists, and the fix is the same.
Sources
Related posts
More in Developers
- Sume 404 resource_not_found: job_not_found and model_not_found overlap
Every Sume 404 reports public_reason resource_not_found, whatever the code. Branch on error.code to tell a missing job from a bad model id or a missing asset.
- Sume 409 job_not_queued: the job left the queue before it started
What the Sume 409 job_not_queued means: a submit found its job no longer queued, sent nothing to the provider, and the error is not retryable by resending.
- Sume generation_capacity_exhausted: the 503, job reason and flag
Sume reports provider capacity three ways: HTTP 503 provider_capacity_exceeded, a job reason generation_capacity_exhausted, and a sync flag. All three retry.
- Sume job failed artifact_too_large: shrink the output, don't rerun
A Sume job failing with artifact_too_large or artifact_upload_rejected made its file but could not store it. Make the file smaller; rerunning changes nothing.
Written by Sume