swagger-api / swagger-api/swagger-parser
[Bug]: OAS 3.1 dereferencer crashes on schemas with + , loses / during resolution
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 867
- Forks
- 560
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 7
Description
Description
The OAS 3.1 dereferencer (OpenAPIDereferencer31 + ReferenceVisitor) has two related issues that together prevent parsing any OpenAPI 3.1 spec that uses $dynamicRef / $dynamicAnchor:
-
Hard crash: When a schema in
components/schemashas both$idand$ref, the dereferencer resolves the$refagainst the$idURI instead of the document root. For refs like#/components/schemas/..., this causes aRuntimeException: Could not find .... -
Data loss:
$dynamicRef,$dynamicAnchor, and$defsare deserialized correctly but are not preserved through the resolution cycle.mergeSchemas()does not copy these fields,$defsis not traversed, andfindAnchor()ignores$dynamicAnchor.
Environment
- swagger-parser version: 2.1.41 (latest release)
- OpenAPI version: 3.1.x
Reproduction
Case 1: $id + $ref crash
openapi: 3.1.0
info:
title: Test
version: 1.0.0
paths: {}
components:
schemas:
Template:
type: object
properties:
items:
type: array
Concrete:
$id: "https://example.com/schemas/Concrete"
$ref: "#/components/schemas/Template"
ParseOptions options = new ParseOptions();
options.setResolve(true);
SwaggerParseResult result = new OpenAPIV3Parser().readLocation("spec.yaml", null, options);
// result.getMessages() contains:
// "Could not find /components/schemas/Template in contents of #/components/schemas/Template"
Case 2: $dynamicRef / $dynamicAnchor lost
openapi: 3.1.0
info:
title: Test
version: 1.0.0
paths: {}
components:
schemas:
Node:
type: object
properties:
children:
type: array
items:
$dynamicRef: "#node"
$defs:
node:
$dynamicAnchor: node
After parsing with setResolve(true), $dynamicRef, $dynamicAnchor, and the $defs contents may not survive the dereference cycle.
Full reproduction specs
Minimal validator-backed OpenAPI 3.1 fixtures demonstrating both issues are available at:
https://github.com/aqeelat/openapi-dynamicref-adoption-tracker
Likely Cause Analysis
Bug 1: $id appears to hijack $ref resolution
In ReferenceVisitor.resolveSchemaRef() (line 230-279), the base URI is computed by chaining inheritedIds (the $id stack) against the current reference URI:
String baseURI = this.reference.getUri();
for (String id: inheritedIds) {
baseURI = ReferenceUtils.resolve(ReferenceUtils.toBaseURI(id), baseURI);
baseURI = ReferenceUtils.toBaseURI(baseURI);
}
baseURI = ReferenceUtils.resolve(ref, baseURI);
When Concrete has $id: "https://example.com/schemas/Concrete" and $ref: "#/components/schemas/Template":
baseURIbecomeshttps://example.com/schemas/Concreteresolve("#/components/schemas/Template", baseURI)yieldshttps://example.com/schemas/Concrete#/components/schemas/Template- The parser looks for JSON Pointer
/components/schemas/Templateinside theConcreteschema's JSON tree Concretehas nocomponentsproperty → crash
In pure JSON Schema, $id changing the base URI is correct. But in an OpenAPI document, #/components/schemas/... is a document-level reference that should resolve against the document root regardless of any schema's $id.
Bug 2: Data is not fully preserved during resolution
| Field | Deserialized? | Preserved by mergeSchemas()? |
Traversed? |
|---|---|---|---|
$dynamicRef |
Yes (getJsonSchema() line 4251) |
No | N/A (not a container) |
$dynamicAnchor |
Yes (getJsonSchema() line 4246) |
No | N/A (not a container) |
$defs |
Not mapped to a dedicated Schema field (retained as raw extension data) | N/A | No (traverseSchema() does not visit $defs) |
Additionally, findAnchor() (line 281-308) only searches for $anchor, not $dynamicAnchor.
Expected Behavior
#/components/schemas/...refs should always resolve against the document root, even when the enclosing schema has$id.$dynamicRef,$dynamicAnchor, and$defsshould survive the parse + dereference cycle so that downstream tooling can access them.$defssub-schemas should be traversed so that any$refor$dynamicAnchorinside them is available in the parsed output.
Impact
This is reproducible in swagger-parser 2.1.41 with the fixtures above and affects OpenAPI 3.1 specs that use JSON Schema 2020-12 features for dynamic polymorphism — specifically the $dynamicRef / $dynamicAnchor pattern for recursive schemas, generic wrappers, and template types as described in:
- OAI issue: https://github.com/OAI/OpenAPI-Specification/issues/3601
- JSON Schema article: https://json-schema.org/blog/posts/dynamicref-and-generics
Related downstream report (OpenAPI Generator, which depends on swagger-parser): https://github.com/OpenAPITools/openapi-generator/issues/13087.
Proposed Fix Scope
This issue is scoped to parser correctness and data preservation only:
- avoid crashes for valid OAS 3.1 documents using
$id+ document-level$ref - preserve
$dynamicRef,$dynamicAnchor, and$defsthrough parse + dereference - traverse
$defssub-schemas so nested references/anchors remain available
It does not request implementation of full JSON Schema 2020-12 dynamic scope resolution.
Related Issues
- #2266 — External schema resolution broken in OpenAPI 3.1 (related
$id+$refinteraction) - #2201 — Ref inside of Items are not resolved (OpenAPI 3.1)
This issue was drafted with assistance from AI tooling. The submitter is responsible for reviewing and validating the contents before submission.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with OpenAPIDereferencer31 and ReferenceVisitor, especially resolveSchemaRef(), findAnchor(), mergeSchemas(), and traverseSchema(). Reproduce both cases with the supplied YAML and inspect getJsonSchema() handling of the fields. Done means document-level refs no longer crash and $dynamicRef, $dynamicAnchor, and traversed $defs survive parsing and dereferencing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100