Describe the bug
When an external schema file uses OpenAPI 3.1 root-level $ref with sibling keywords (type, title, properties, required, etc.), redocly bundle resolves that file to only the $ref target and discards the siblings.
References to that schema are then rewritten to the target schema. Intermediate schemas effectively disappear from the useful bundled model (or remain as a pure $ref alias with no siblings).
redocly lint does not report a problem on the source; the loss happens at bundle time.
OpenAPI 3.1 context (Reference Object vs Schema Object)
This report is specifically about Schema Objects (data models), not Reference Objects elsewhere in the document.
In OpenAPI 3.1+:
- Reference Objects outside Schema Objects (for example path item, parameter, response, or request body refs) are still Reference Objects. Sibling keywords next to
$ref are restricted (notably summary and description).
- Schema Objects adopt JSON Schema Draft 2020-12. In that model,
$ref may appear alongside other schema keywords, and those siblings are applied together with the referenced schema (composition), not ignored.
So a schema like:
title: Bad request
type: object
$ref: ./BaseProblem.yaml
properties:
status:
type: integer
required:
- status
is a valid OAS 3.1 Schema Object / JSON Schema 2020-12 schema. Bundling should preserve that composition, not treat the root $ref as a full replacement the way a non-schema Reference Object might be inlined.
Repro
# openapi.yaml
openapi: 3.1.0
info:
title: file $ref sibling repro
version: 1.0.0
paths:
/demo:
get:
operationId: getDemo
responses:
'400':
description: Bad request
content:
application/json:
schema:
$ref: ./schemas/BadRequest.yaml
components:
schemas:
BadRequest:
$ref: ./schemas/BadRequest.yaml
BaseProblem:
$ref: ./schemas/BaseProblem.yaml
# schemas/BaseProblem.yaml
type: object
properties:
title:
type: string
# schemas/BadRequest.yaml
title: Bad request
type: object
$ref: ./BaseProblem.yaml
properties:
status:
type: integer
minimum: 400
maximum: 400
required:
- status
npx @redocly/cli@2.38.0 bundle openapi.yaml --ext json
# also reproduced with 2.39.0 and 2.40.0
Actual
"BadRequest": { "$ref": "#/components/schemas/BaseProblem" }
Path/response 400 schema also points at BaseProblem. Sibling keywords from BadRequest.yaml (title, type, properties.status, required) are gone.
Expected
Bundled BadRequest keeps sibling keywords composed with BaseProblem (or an equivalent merged/allOf form), and references keep pointing at BadRequest.
Control
The same API expressed with allOf instead of root $ref + siblings bundles correctly (BadRequest retained; refs point at BadRequest).
Property-level $ref + siblings (for example writeOnly next to a property $ref) appear to bundle correctly in our larger spec. The failure we hit is specific to root-level $ref + siblings on an external schema file.
Independent validation (not Redocly)
We validated the same $ref + siblings composition with popular JSON Schema Draft 2020-12 implementations. Sibling keywords are applied, not ignored:
Schema under test (equivalent form):
$ref to BaseProblem
- sibling
properties.status (minimum/maximum 400)
- sibling
required: ["status"]
| Instance |
Ajv (2020-12) |
@hyperjump/json-schema |
Python jsonschema (Draft202012Validator) |
{ "title": "Bad request", "status": 400 } |
PASS |
PASS |
PASS |
{ "title": "Bad request" } (missing status) |
FAIL |
FAIL |
FAIL |
{ "title": "Bad request", "status": 500 } |
FAIL |
FAIL |
FAIL |
Ajv also compiles the schema successfully against the Draft 2020-12 meta-schema.
If the schema is collapsed to Redocly’s bundled form ({ "$ref": "..." } only), Ajv accepts the missing-status instance — i.e. the sibling constraints are lost. That matches the buggy bundle output and shows the bundler is dropping JSON Schema semantics that other validators enforce.
Separately, @apidevtools/swagger-parser accepts the OpenAPI 3.1 document; after dereference, BadRequest still retains properties.status.
Environment
@redocly/cli 2.38.0, 2.39.0, 2.40.0
- OpenAPI 3.1.0 / 3.1.1
Notes
Possibly related to sibling traversal work in #2768 / #2772 / #1907 / #2362, but those focus on resolving refs inside siblings. Here sibling keywords on an external schema file root are dropped entirely when that root $ref is resolved.
Describe the bug
When an external schema file uses OpenAPI 3.1 root-level
$refwith sibling keywords (type,title,properties,required, etc.),redocly bundleresolves that file to only the$reftarget and discards the siblings.References to that schema are then rewritten to the target schema. Intermediate schemas effectively disappear from the useful bundled model (or remain as a pure
$refalias with no siblings).redocly lintdoes not report a problem on the source; the loss happens at bundle time.OpenAPI 3.1 context (Reference Object vs Schema Object)
This report is specifically about Schema Objects (data models), not Reference Objects elsewhere in the document.
In OpenAPI 3.1+:
$refare restricted (notablysummaryanddescription).$refmay appear alongside other schema keywords, and those siblings are applied together with the referenced schema (composition), not ignored.So a schema like:
is a valid OAS 3.1 Schema Object / JSON Schema 2020-12 schema. Bundling should preserve that composition, not treat the root
$refas a full replacement the way a non-schema Reference Object might be inlined.Repro
npx @redocly/cli@2.38.0 bundle openapi.yaml --ext json # also reproduced with 2.39.0 and 2.40.0Actual
Path/response
400schema also points atBaseProblem. Sibling keywords fromBadRequest.yaml(title,type,properties.status,required) are gone.Expected
Bundled
BadRequestkeeps sibling keywords composed withBaseProblem(or an equivalent merged/allOfform), and references keep pointing atBadRequest.Control
The same API expressed with
allOfinstead of root$ref+ siblings bundles correctly (BadRequestretained; refs point atBadRequest).Property-level
$ref+ siblings (for examplewriteOnlynext to a property$ref) appear to bundle correctly in our larger spec. The failure we hit is specific to root-level$ref+ siblings on an external schema file.Independent validation (not Redocly)
We validated the same
$ref+ siblings composition with popular JSON Schema Draft 2020-12 implementations. Sibling keywords are applied, not ignored:Schema under test (equivalent form):
$reftoBaseProblemproperties.status(minimum/maximum400)required: ["status"]@hyperjump/json-schemajsonschema(Draft202012Validator){ "title": "Bad request", "status": 400 }{ "title": "Bad request" }(missingstatus){ "title": "Bad request", "status": 500 }Ajv also compiles the schema successfully against the Draft 2020-12 meta-schema.
If the schema is collapsed to Redocly’s bundled form (
{ "$ref": "..." }only), Ajv accepts the missing-statusinstance — i.e. the sibling constraints are lost. That matches the buggy bundle output and shows the bundler is dropping JSON Schema semantics that other validators enforce.Separately,
@apidevtools/swagger-parseraccepts the OpenAPI 3.1 document; after dereference,BadRequeststill retainsproperties.status.Environment
@redocly/cli2.38.0, 2.39.0, 2.40.0Notes
Possibly related to sibling traversal work in #2768 / #2772 / #1907 / #2362, but those focus on resolving refs inside siblings. Here sibling keywords on an external schema file root are dropped entirely when that root
$refis resolved.