output_schema_invalid 400: read violations path and rule tokens

A Sume Format run with a schema outside the supported subset returns 400 output_schema_invalid with every violation listed. Switch on rule, never on message.

5 min readSume
All posts

When the output_schema on a Sume Format run is outside the supported subset, the create call returns 400 output_schema_invalid and nothing runs, so nothing is charged. The body carries details.violations[], each entry { path, rule, message }, and every problem is reported at once, not just the first.

Fix a schema from one response by branching on rule, the stable lowercase token, and using path to find the node. message is written for a human and may change, so never match on it.

What is in a violation?

path is a JSON-Pointer-style location such as #/properties/scenes/items/properties/clip. rule is the token documented on the structured output page, safe to switch on. message explains the problem in words and is for logs.

Because all violations arrive together, a schema ported from another provider usually needs one round trip, not ten. The most common batch is a trio: an object missing additionalProperties: false, a property missing from required, and an oneOf that should be anyOf.

violation rule tokens and the usual fix, read 2026-10-02
ruleMeaningUsual fix
root_must_be_objectRoot is not exactly type objectWrap arrays or unions in an object property
missing_typeNode has no type, $ref or anyOfAdd a type
unsupported_keywordKeyword is off the allowlistReplace oneOf with anyOf, flatten allOf
additional_properties_falseObject lacks additionalProperties falseAdd it on every object, including $defs
required_completenessDeclared property not in required, or the reverseList every property in required; use a nullable union
missing_itemsArray has no itemsDeclare items
unsupported_ref$ref is not #/$defs/name or SumeMediaFile#Move definitions to root $defs
invalid_defs$defs is not an object of schemasFix the $defs block

How do I turn violations into fixes in code?

The sample maps each rule token to a one-line hint and prints the path next to it. It runs as written against a sample error body, and it falls through on a token it does not know, which matters because the documented size limits and other rules can grow.

Keep your own hint table small and let unknown tokens print the server's message, so a new rule still reaches you.

import json

HINTS = {
    "root_must_be_object": "wrap it in an object property",
    "unsupported_keyword": "use anyOf instead of oneOf; flatten allOf",
    "additional_properties_false": "add additionalProperties: false",
    "required_completeness": "list every property in required",
    "missing_items": "declare items on the array",
}

err = json.loads('''{"error": {"code": "output_schema_invalid", "details": {"violations": [
  {"path": "#", "rule": "additional_properties_false", "message": "open object"},
  {"path": "#/properties/a", "rule": "unsupported_keyword", "message": "oneOf"},
  {"path": "#/properties/b", "rule": "new_rule", "message": "see docs"}]}}}''')

for v in err["error"]["details"]["violations"]:
    print(v["path"], "->", HINTS.get(v["rule"], v["message"]))

Does strict false skip any of these checks?

No. strict: false is accepted and stored, and it changes nothing: a schema outside the subset is rejected whether strict is true or false. There is also no JSON mode equivalent, so the choices are a bound schema or the built-in schema.

If you are porting a schema from another provider, read the OpenAI response_format post and the optional field post, which cover the two ports that fail most often.

What if the schema is valid and the run still fails?

Then you are no longer at submit. A schema that passes validation can still produce output_schema_unsatisfied after the run, with violations[] or rejected_urls[] in output_error.details. That is a failure of the finished run to fit your schema, and it is read from the receipt, not from a 400. A 400 here costs nothing; a post-run failure may already have billed generation.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume