swagger-api / swagger-api/swagger-parser

[Bug]: OpenAPI 3.1 Parser Unexpectedly Merges Identical Inline Objects When resolve=true

Open
#2,264 0 comments 1 reaction 2 assignees View on GitHub

@daniel-kmiecik is already working on this.

Since Jan 30, 2026.

Bug
Dominant language
Java
Stars
867
Forks
560
Avg merge
2d 21h
Merged PRs (30d)
7

Description

Description

When parsing an OpenAPI 3.1 specification with ParseOptions.setResolve(true), the OpenAPIDereferencer31 unexpectedly merges identical inline objects (schemas, responses, etc.) into the same object reference. This behavior is inconsistent with the OpenAPI specification and causes issues for code generation tools.

Affected Version

2.1.37

Steps to Reproduce

Test OpenAPI 3.1 Spec (swagger.json):

{
  "openapi": "3.1.0",
  "info": {
    "title": "API",
    "version": "1.0"
  },
  "paths": {
    "/path1": {
      "post": {
        "responses": {
          "200": {
            "description": "result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string" },
                    "status": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/path2": {
      "post": {
        "responses": {
          "200": {
            "description": "result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": { "type": "string" },
                    "status": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Test Code:

// With resolve=true
ParseOptions options1 = new ParseOptions();
options1.setResolve(true);
SwaggerParseResult result1 = new OpenAPIParser().readLocation("swagger.json", null, options1);

ApiResponse path1Response = result1.getOpenAPI().getPaths().get("/path1").getPost().getResponses().get("200");
ApiResponse path2Response = result1.getOpenAPI().getPaths().get("/path2").getPost().getResponses().get("200");

System.out.println(path1Response == path2Response);  // Prints: true (UNEXPECTED!)

// With resolve=false
ParseOptions options2 = new ParseOptions();
options2.setResolve(false);
SwaggerParseResult result2 = new OpenAPIParser().readLocation("swagger.json", null, options2);

ApiResponse path1Response2 = result2.getOpenAPI().getPaths().get("/path1").getPost().getResponses().get("200");
ApiResponse path2Response2 = result2.getOpenAPI().getPaths().get("/path2").getPost().getResponses().get("200");

System.out.println(path1Response2 == path2Response2);  // Prints: false (EXPECTED)

In OpenAPI31Traverser.java, the traversal logic uses:

if (visitedMap.containsKey(mediaType)) {
    return (MediaType)visitedMap.get(mediaType);  // Returns cached object
}

Since MediaType, Schema, ApiResponse, and other model classes override equals() to compare content rather than object identity, the visitedMap incorrectly identifies different inline objects as the same when their content matches.

Expected Behavior

According to the OpenAPI 3.1 specification, two inline objects with identical content should remain as separate, independent objects unless they are explicitly referenced using $ref. Each inline definition should maintain its own object identity.

Actual Behavior

When resolve=true is enabled, the OpenAPI31Traverser uses a visitedMap (HashMap) that relies on the equals() method to cache visited objects. When it encounters a second inline object with identical content, it returns the cached reference from the first object instead of treating them as separate entities.

Additional Context

This issue only affects OpenAPI 3.1 parsing with resolve=true. The older OpenAPI 3.0 resolver (OpenAPIResolver) does not exhibit this behavior. The issue was introduced with the new OpenAPIDereferencer31 implementation.

The DISABLE_OAS31_RESOLVE environment variable can be used as a workaround to disable OAS 3.1 resolution, but this prevents proper $ref resolution as well.

Checklist

  • I have searched the existing issues and this is not a duplicate.
  • I have provided sufficient information for maintainers to reproduce the issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.