Format output_schema: $ref "#" fails, a $defs self-reference works

Sume rejects root recursion via $ref "#" in output_schema as unsupported_ref but accepts a $defs entry that references itself. How to write a tree.

5 min readSume
All posts

For a recursive result such as a scene tree, put the repeating shape in a root-level $defs entry and let that entry $ref itself. Sume's Format output_schema accepts that, but it refuses the shortcut "$ref": "#" that points at the whole document, with 400 output_schema_invalid and the rule unsupported_ref.

The facts come from the Structured output page. OpenAI's Structured model outputs page, read 2026-10-03, shows recursive schemas using $ref and $defs, so the named-definition style travels between the two.

Which $ref targets resolve?

Only two targets resolve in Sume. Anything else, including a URL or a path under #/components, is rejected.

Resolvable $ref targets, from Sume's Structured output page (read 2026-10-03)
TargetUseNotes
#/$defs/*Your own definitions$defs must be declared at the root of the schema document
SumeMediaFile#Sume's media shapeFor video, image, audio or file fields in output
#Not acceptedRoot recursion is permitted by OpenAI strict mode; Sume does not allow it
External URL or sibling documentNot acceptedunsupported_ref

Why does a nested $defs block fail?

A #/$defs/* target with no matching entry at the root is rejected too. That catches a $defs block tucked inside a sub-schema, which is the usual mistake when a schema is assembled from fragments. Move it to the top level, next to type and properties.

$ref and anyOf also short-circuit the node they sit on. Sibling keywords are checked against the allowlist but carry no meaning, so put constraints inside the $defs entry or the anyOf branches, not beside the $ref.

How do I write a recursive definition that is accepted?

The docs state that recursion through a named definition is fine: a $defs entry may $ref itself. Because a self-reference is not literal nesting, it does not consume the 10-level depth limit.

The sketch below describes a section that holds a title and a list of child sections. The recursion is the children array, whose items point back at the same definition; an empty array ends it, so no property needs to be optional. Every property stays required and every object stays closed. Treat it as a starting point and send it to a test run before depending on it.

{
  "type": "object",
  "additionalProperties": false,
  "required": ["root"],
  "properties": { "root": { "$ref": "#/$defs/section" } },
  "$defs": {
    "section": {
      "type": "object",
      "additionalProperties": false,
      "required": ["title", "children"],
      "properties": {
        "title": { "type": "string" },
        "children": { "type": "array", "items": { "$ref": "#/$defs/section" } }
      }
    }
  }
}

What still limits a recursive schema?

The size limits apply to the document you send, not to the runs it permits: 10 levels of literal nesting, 5000 properties and 120,000 characters of strings, summed across the document. They report as max_depth, max_properties and max_string_length.

Sume does not publish a cap on how deep the run's own tree may go, so keep recursive output shallow in the instruction. If a run cannot fill the shape, the failure arrives later on the receipt as output_schema_unsatisfied, with the media still in artifacts[].

What does the rejection look like?

A schema that uses "$ref": "#" returns 400 output_schema_invalid with a violation whose rule is unsupported_ref. The rule's definition in the docs covers any $ref that is neither #/$defs/<name> nor SumeMediaFile#, and also one that names a definition that does not exist.

The fix is mechanical: lift the repeating shape into root $defs, give it a name, and point both the entry point and the recursive property at #/$defs/<name>. Nothing else in the schema needs to change.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume