OpenAPITools / OpenAPITools/openapi-generator

[BUG][openapi-yaml] Merged spec reorders HTTP operations under the same path, breaking intended method ordering

Open
#23,009 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

When using the openapi-yaml generator to produce a merged (flattened) OpenAPI specification with all $ref references resolved, the HTTP operations (methods) under each path are reordered alphabetically instead of preserving the original declaration order from the source YAML file.

For example, if a path defines operations in the order GET, POST, PUT, PATCH, DELETE, the merged output reorders them to DELETE, GET, PATCH, POST, PUT. This silently breaks the intended ordering despite the source OpenAPI files being defined correctly.

We maintain internal API documentation conventions that prescribe a specific HTTP method order (e.g., CRUD: GET -> POST -> PUT -> PATCH -> DELETE). The merged spec is used as the source of truth for downstream API consumers and documentation tools (e.g., Swagger). Because the generator reorders operations, consumers see them in an unexpected sequence, leading to confusion and inconsistency between the authored spec and the rendered documentation.

openapi-generator version

Reproduced with v7.14.0. The issue likely affects all versions of the openapi-yaml generator that do not set skipSortingOperations.

OpenAPI declaration file content or url

Input spec (sample-api-v1.yaml):

openapi: 3.0.3
info:
  title: Sample API
  version: 1.0.0
tags:
  - name: orders
    description: Order management operations
paths:
  /orders:
    get:
      tags:
        - orders
      summary: List all orders
      operationId: listOrders
      responses:
        '200':
          description: successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Order'
    post:
      tags:
        - orders
      summary: Create a new order
      operationId: createOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Order'
      responses:
        '201':
          description: order created
  /orders/{orderId}:
    get:
      tags:
        - orders
      summary: Get order by ID
      operationId: getOrderById
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
    put:
      tags:
        - orders
      summary: Update an order
      operationId: updateOrder
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Order'
      responses:
        '200':
          description: order updated
    patch:
      tags:
        - orders
      summary: Partially update an order
      operationId: patchOrder
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Order'
      responses:
        '200':
          description: order patched
    delete:
      tags:
        - orders
      summary: Delete an order
      operationId: deleteOrder
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: order deleted
components:
  schemas:
    Order:
      type: object
      required:
        - id
      properties:
        id:
          type: string
        description:
          type: string
        status:
          type: string
          enum:
            - pending
            - completed
            - cancelled
Generation Details
java -jar openapi-generator-cli.jar generate \
  -i sample-api-v1.yaml \
  -g openapi-yaml \
  -o ./output

Or via Gradle plugin:

tasks.register<GenerateTask>("generateMergedSpec") {
    generatorName.set("openapi-yaml")
    inputSpec.set("$projectDir/api/sample-api-v1.yaml")
    outputDir.set("${layout.buildDirectory.get().asFile}/merged-spec")
    configOptions.put("outputFile", "sample-api-v1.yaml")
}
Steps to reproduce
  1. Save the YAML spec above as sample-api-v1.yaml.
  2. Generate the merged spec using the openapi-yaml generator (via CLI or Gradle plugin).
  3. Open the generated output file (e.g. build/merged-spec/sample-api-v1.yaml for the Gradle example, or output/openapi/openapi.yaml for the CLI).
  4. Inspect the HTTP methods under /orders/{orderId}.

Expected: Operations preserve the source file order:

  /orders/{orderId}:
    get:
      ...
    put:
      ...
    patch:
      ...
    delete:
      ...

Actual: Operations are reordered alphabetically by HTTP method:

  /orders/{orderId}:
    delete:
      ...
    get:
      ...
    patch:
      ...
    put:
      ...

This causes documentation tools to display operations in an unintuitive order (DELETE before GET), creating confusion for API consumers who see a different ordering than what was intentionally defined in the source spec.

Related issues/PRs
  • #22166 This issue was fixed for python-fastapi by enabling skipSortingOperations, but the same problem exists in the openapi-yaml generator.
  • #12950 Open feature request for similar behavior in the HTML generator.
  • #19550 Related fix for another generator addressing path operation ordering.
Suggest a fix

The openapi-yaml generator (OpenAPIYamlGenerator) should override getSkipSortingOperations() to return true by default, similar to the fix applied in #22163 for python-fastapi.

The openapi-yaml generator is primarily used to resolve $ref references and produce a flat, self-contained YAML file. Sorting operations is counterproductive in this use case, as the purpose is to faithfully represent the original spec with resolved references, not to rearrange it.

Alternatively, a global skipSortingOperations config option could be exposed so users of any generator can opt out of operation sorting.

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.

Research direction

Start in the OpenAPIYamlGenerator implementation and inspect how getSkipSortingOperations is handled for the openapi-yaml generator. Reproduce the issue with the supplied sample-api-v1.yaml and CLI or Gradle commands, then verify that the generated methods under /orders/{orderId} retain the source order while references are still resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
api, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.