Skip to content

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

@harryeti

Description

@harryeti

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.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions