Sume 400 invalid_request: read details.errors[].path to find the field

A Sume 400 invalid_request lists each bad field in details.errors with path, loc, message and type. Read them in Python instead of guessing from the message.

4 min readSume
All posts

When a Sume request fails schema validation you get HTTP 400 with error.code set to invalid_request, the message Invalid request body, parameters, or headers., and the real answer in error.details.errors: one object per failing field, each with path, loc, message and type. Read path and you know which field to fix before you resend anything.

What does the 400 body contain?

The API code that builds this response turns each validator issue into four keys. path is the field as a dotted string, loc is the same path as an array, message is the validator's sentence, and type is the validator keyword such as required, enum or type. For a missing required property the property name is appended to the path, so a body with no prompt reports prompt. The envelope around it is the normal one: request_id, category: validation, retryable: false and next_action: fix_input.

One wrinkle: the example on the docs errors page shows details as a list. The API code builds an object with an errors array, and its own tests assert details.errors[0].path on asset registration and on a trending-videos search. Parse both shapes defensively and you will survive either.

How do I print the failing fields?

This function takes the parsed JSON of any Sume error and returns readable lines. It also runs on an empty or non-validation body.

def field_errors(body: dict) -> list[str]:
    err = body.get("error") or {}
    details = err.get("details")
    if isinstance(details, dict):
        items = details.get("errors") or []
    elif isinstance(details, list):
        items = details
    else:
        items = []
    lines = []
    for item in items:
        if not isinstance(item, dict):
            continue
        path = item.get("path") or ".".join(map(str, item.get("loc", [])))
        lines.append(f"{path or '(body)'}: {item.get('message', 'invalid')} [{item.get('type', '?')}]")
    if not lines:
        lines.append(f"{err.get('code', 'unknown')}: {err.get('message', '')}")
    return lines


sample = {"error": {"code": "invalid_request", "details": {"errors": [
    {"path": "prompt", "loc": ["prompt"], "message": "must have required property 'prompt'", "type": "required"}]}}}
print("\n".join(field_errors(sample)))

Which 400s are not this one?

Some 400s are named. A model id that is not in the catalog on the image, TTS Router and Music Router routes returns model_not_found with a catalog_url; a bad Timeline transition.type, fit or output.fps returns invalid_transition_type, invalid_fit or invalid_fps with the allowed values; a Video filter program returns codes such as unsupported_filter_op. A body sent without a JSON content type is a 415, not a 400. Check error.code first and fall back to details.errors only for invalid_request.

400-class codes and where the fix is written, from the Sume API error handler and docs.sume.com errors page, read 2026-10-11.
error.codeStatusWhere the fix is
invalid_request400details.errors[].path and message
model_not_found400 or 404 by routedetails.catalog_url lists accepted ids
invalid_transition_type, invalid_fit, invalid_fps400details.allowed and details.field
unsupported_media_type415details.received_content_type; send application/json
payload_too_large413details.limit_bytes; shrink the body

Should I retry after a 400?

No. retryable is false and nothing was started, so resending the same body returns the same 400. Fix the named field, then send the corrected request. If the response also carries a request_id and status_url in details, a job row already exists; see the related post on a 400 that returns a job id.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume