Sume images 400: read details.supported and retry in Python
A 400 invalid_request from Sume's image API lists the accepted values in details.supported. Parse it in Python, then choose a value on purpose.

When Sume's image API rejects a value the model does list as a field, it returns 400 invalid_request and puts the accepted values in error.details.supported (or the range in details.min and details.max). You can read that list in code and decide what to resend, instead of guessing from the model docs. When the model does not list the field at all, the code is unsupported_parameter instead, and there is no list to read.
The behavior below comes from running the request normalizer in Sume's repository on 2026-10-03; the rule that a parameter a model does not list is rejected rather than silently dropped is on the Image API page.
What does each 400 tell me?
Two different 400 codes are easy to confuse. unsupported_parameter says the field is not advertised by that model, and points at /v1/images/models through details.catalog_url. invalid_request says the field exists but your value is outside what the model accepts, and details.supported holds the allowed values.
| Request | Code | What the error carries |
|---|---|---|
openai/gpt-image-2 with quality: "xhigh" | invalid_request | supported: low, medium, high |
x-ai/grok-image with aspect_ratio: "4:5" | invalid_request | supported: 13 ratios, from 2:1 to 9:20 |
x-ai/grok-image with n: 2 | invalid_request | min: 1, max: 1 |
recraft/recraft-v4 with output_format: "png" | invalid_request | supported: webp |
google/nano-banana-2 with quality: "high" | unsupported_parameter | catalog_url: /v1/images/models |
gpt-image-2.5-flare as the model | model_not_found (404) | catalog_url: /v1/images/models |
How do I handle it in Python?
Return the field and the list, and let the caller choose. Do not auto-pick the first entry: for quality that would quietly buy a cheaper image than you asked for, and for aspect_ratio it could change the crop. The function below only extracts the information; the sample body is the real shape for the xhigh row above.
def accepted_values(body):
err = body.get("error") or {}
details = err.get("details") or {}
if err.get("code") != "invalid_request" or not isinstance(details, dict):
return None
if "supported" in details:
return details.get("field"), list(details["supported"])
if "min" in details and "max" in details:
return details.get("field"), list(range(details["min"], details["max"] + 1))
return None
sample = {"error": {"code": "invalid_request",
"message": 'openai/gpt-image-2 does not accept quality "xhigh".',
"details": {"field": "quality", "supported": ["low", "medium", "high"]}}}
print(accepted_values(sample)) # ('quality', ['low', 'medium', 'high'])Should I retry a 400 automatically?
Only with a change you chose. A 400 is a validation failure, and the errors page lists validation as a category whose next action is to fix the input. Resending the same body repeats the same rejection, and the image page says failed generations are not billed.
A practical pattern is to check the catalog first: fetch GET /v1/images/models, read each model's supported_parameters, and validate your payload before you submit. Keep the 400 handler as a backstop for catalog changes, and log request_id from the error body so a rejected call can be traced.
Where do the lists come from?
The values in details.supported are the same descriptors the catalog publishes. The Image API page describes three kinds: enum (a discrete allowlist of strings), range (any integer between a minimum and maximum), and boolean (supported or absent). Because the error and the catalog are built from one source, a value you read from GET /v1/images/models is a value the 400 handler will accept.
That also means the error list can differ by model and over time. Do not copy a list from one model into another model's validation. Cache the catalog for the length of one batch run, and refetch at the start of the next.
Sources
Related posts
More in Developers
- Image API provider.only returns 400 provider_not_available on Sume
Sume's Image API accepts provider routing fields but publishes one sume endpoint per model, so any other provider slug returns 400 provider_not_available.
- Inngest 1.45 returns 400 for unknown v2 fields: treat a Sume 400 alike
Inngest v1.45.0 rejects unmapped REST v2 request fields with HTTP 400. A Sume 400 invalid_request is the same fix-the-request signal; do not retry it.
- Insomnia: import the Sume OpenAPI from a URL and set up auth
Insomnia Import then URL accepts an OpenAPI 3.0 or 3.1 spec. Paste api.sume.com/reference/json, then keep the Bearer key in an environment, not the spec.
- Inso CLI in CI: lint and export a Sume OpenAPI copy in Insomnia
Inso CLI can lint an OpenAPI spec and export one from an Insomnia collection, failing a build on errors. Use it to guard your committed copy of the Sume spec.
Written by Sume