Repository navigation
[Bug]: Array items $ref to an external file is not resolved to #/components/schemas/... when it's the only $ref in the referencing file #2409
Description
Description
When a component schema is externalized to its own file and that file contains an array whose items is a $ref
to another external file, the items $ref is left as the raw relative file path (./schemas/Item.yaml) instead of
being resolved to #/components/schemas/Item, unless the same external file also has a top-level property that is itself a bare $ref.
There is no entry in SwaggerParseResult#getMessages(). With resolveFully=true the items ref is also not
inlined. Because of this, the openapi-generator library cannot resolve the leftover file ref and generates List<Object> instead of List<Item> (it logs Failed to get the schema name: ./schemas/Item.yaml and
Error obtaining the datatype from ref: ./schemas/Item.yaml. Default to 'object').
Affected Version
2.1.48
Earliest version the bug appears in (if known):
2.1.33 (couldn't test older versions than that)
Steps to Reproduce
Note that the spec is 3.0 not 3.1, 3.1 doesn't appear to have this problem.
openapi.yaml
openapi: 3.0.3
info:
title: Array items $ref repro
version: "1"
paths:
/broken:
get:
operationId: getBroken
responses:
"200":
description: ok
content:
application/json:
schema:
$ref: "#/components/schemas/Broken"
/working:
get:
operationId: getWorking
responses:
"200":
description: ok
content:
application/json:
schema:
$ref: "#/components/schemas/Working"
components:
schemas:
Item:
$ref: "./schemas/Item.yaml"
Broken:
$ref: "./schemas/Broken.yaml"
Working:
$ref: "./schemas/Working.yaml"schemas/Item.yaml
type: object
properties:
name:
type: stringschemas/Broken.yaml (the array's items is the only $ref in the file)
type: object
properties:
items:
type: array
items:
$ref: "./Item.yaml"schemas/Working.yaml (identical, plus one unrelated top-level property $ref)
type: object
properties:
items:
type: array
items:
$ref: "./Item.yaml"
extra:
$ref: "./Item.yaml"Run this code:
ParseOptions options = new ParseOptions();
options.setResolve(true);
SwaggerParseResult result = new OpenAPIV3Parser().readLocation("openapi.yaml", null, options);
Schema broken = result.getOpenAPI().getComponents().getSchemas().get("Broken");
Schema working = result.getOpenAPI().getComponents().getSchemas().get("Working");
System.out.println(((Schema) broken.getProperties().get("items")).getItems().get$ref());
System.out.println(((Schema) working.getProperties().get("items")).getItems().get$ref());Expected Behavior
Both print #/components/schemas/Item (or, with resolveFully=true, both are inlined as an object schema with
name).
Actual Behavior
With setResolve(true):
| Schema | items.getItems().get$ref() |
|---|---|
Broken |
./schemas/Item.yaml (not resolved) |
Working |
#/components/schemas/Item |
With setResolve(true) + setResolveFully(true): Working is inlined, Broken is unchanged
(./schemas/Item.yaml). getMessages() is empty in all cases, and components.schemas contains [Item, Broken, Working] with no extra keys. This behavior is unexpected and not self-consistent.
Logs / Stack Traces
No parser warning at debug level.
However, openapi-generator logs messages like this:
[WARNING] ./schemas/Item.yaml is not defined
[WARNING] Failed to get the schema name: ./schemas/Item.yaml
[WARNING] Error obtaining the datatype from ref: ./schemas/Item.yaml. Default to 'object'
Environment
- Java version:
- Build tool:
- OS:
Additional Context
OpenAPITools/openapi-generator#2249 might share the same root cause, but it's not obvious
Checklist
- I have searched the existing issues and this is not a duplicate.
- I have provided sufficient information for maintainers to reproduce the issue.
Activity
vinirib commentedon Oct 29, 2026
I reproduced this on current
master(2.1.49-SNAPSHOT). The property name is not a factor: renamingitemsto something else fails the same way.Root cause is the second pass in
ComponentsProcessor.processSchemas. After the first pass,ExternalRefProcessorleaves refs between external files rewritten relative to the root (./Item.yamlbecomes./schemas/Item.yaml), and the second pass is what turns them into#/components/schemas/.... That pass only reprocesses a model when one of its direct properties has a$ref:In
Broken.yamlthe only pending ref sits inside the arrayitems, one level down, so the model is skipped. InWorking.yamlthe bareextraref triggers a reprocess of the whole model, which resolvesitemsas a side effect.I'd like to work on this. The fix I have in mind reprocesses a model once when it has a pending external ref at any depth (properties,
items,additionalProperties,allOf/anyOf/oneOf,not), with a visited set to stay safe on recursive schemas. I'll open a PR with tests covering bothresolveandresolveFully.