{"record":{"id":"17b9ffc5ca9c62e1","repo":"OpenAPITools/openapi-generator","slug":"operationid-conflict-during-spec-merge-s-s","errorCode":null,"errorMessage":"operationId conflict during spec merge: '%s' (%s %s) is already used by another operation.","messagePattern":"operationId conflict during spec merge: '(.+?)' \\((.+?) (.+?)\\) is already used by another operation\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java","lineNumber":642,"sourceCode":"        }\n        Set<String> usedOperationIds = new HashSet<>();\n        for (Map.Entry<String, PathItem> pathEntry : merged.getPaths().entrySet()) {\n            PathItem pathItem = pathEntry.getValue();\n            if (pathItem == null || pathItem.readOperationsMap() == null) {\n                continue;\n            }\n            for (Map.Entry<PathItem.HttpMethod, Operation> opEntry : pathItem.readOperationsMap().entrySet()) {\n                Operation operation = opEntry.getValue();\n                String operationId = operation.getOperationId();\n                if (operationId == null || operationId.isEmpty()) {\n                    continue;\n                }\n                if (!usedOperationIds.add(operationId)) {\n                    String message = String.format(Locale.ROOT,\n                            \"operationId conflict during spec merge: '%s' (%s %s) is already used by another operation.\",\n                            operationId, opEntry.getKey(), pathEntry.getKey());\n                    if (conflictStrategy == MergeConflictStrategy.FAIL) {\n                        throw new RuntimeException(message);\n                    }\n                    String uniqueId = operationId;\n                    int suffix = 2;\n                    while (!usedOperationIds.add(uniqueId + \"_\" + suffix)) {\n                        suffix++;\n                    }\n                    uniqueId = uniqueId + \"_\" + suffix;\n                    LOGGER.warn(\"{} Renaming to '{}'.\", message, uniqueId);\n                    operation.setOperationId(uniqueId);\n                }\n            }\n        }\n    }\n\n    /**\n     * Pushes a spec's root-level security requirements down onto each of its operations that does\n     * not already declare operation-level security. In OpenAPI, root-level {@code security} applies\n     * to every operation unless overridden; an explicit empty list on an operation disables security.","sourceCodeStart":624,"sourceCodeEnd":660,"githubUrl":"https://github.com/OpenAPITools/openapi-generator/blob/fcec517be3cf5b7964296bcba25fbc97541484e7/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java#L624-L660","documentation":"MergedSpecBuilder throws this when two operations across the OpenAPI spec files being merged share the same operationId and mergeConflictStrategy is FAIL. operationIds must be unique in a merged document because they become generated client method names. With the default WARN strategy the duplicate is instead renamed to '<id>_2' and only a warning is logged.","triggerScenarios":"Running openapi-generator in directory merge mode (inputSpec is a directory of .yaml/.yml/.json specs) with conflictStrategy=FAIL, where e.g. both petstore.yaml and store.yaml contain operationId 'getUser'. The second usedOperationIds.add(operationId) returns false and the RuntimeException aborts the merge.","commonSituations":"Microservice specs that each define generic CRUD names like 'list' or 'getById'; copying an existing spec as a template and forgetting to rename operationIds; enabling the strict FAIL strategy in CI to surface overlaps between team-owned spec files.","solutions":["Make the operationId unique across all merged files by prefixing it with its domain, e.g. store_getUser vs pet_getUser.","Re-run with the default WARN conflict strategy so the duplicate is auto-renamed to <id>_2 (acceptable when collisions are benign).","Exclude the overlapping spec file from the merge input directory or list if it was included by accident."],"exampleFix":"# specs/store.yaml + specs/pet.yaml (before) — both files:\noperationId: getUser\n# after — specs/store.yaml:\noperationId: store_getUser\n# after — specs/pet.yaml:\noperationId: pet_getUser","handlingStrategy":"validation","validationCode":"// Pre-flight: fail on duplicate operationIds across spec files before merging\nimport io.swagger.v3.oas.models.OpenAPI;\nimport io.swagger.parser.v3.OpenAPIV3Parser;\nimport java.util.*;\n\nSet<String> seen = new HashSet<>();\nfor (String file : specFiles) {\n    OpenAPI api = new OpenAPIV3Parser().read(file);\n    api.getPaths().forEach((path, item) ->\n        item.readOperationsMap().forEach((method, op) -> {\n            String id = op.getOperationId();\n            if (id != null && !seen.add(id))\n                throw new IllegalStateException(\"Duplicate operationId '\" + id + \"' in \" + file);\n        }));\n}","typeGuard":null,"tryCatchPattern":"try {\n    mergedSpec = mergedSpecBuilder.build();\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"operationId conflict during spec merge\")) {\n        // extract the operationId from the message, report owning spec, fix at source\n        throw new BuildException(\"Spec merge conflict: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Namespace operationIds per spec file (e.g. store_, pet_) before contributing specs to a shared merge directory.","Run the strict FAIL conflict strategy in CI so overlaps are caught before generated clients ship duplicated method names.","Add a pre-merge lint step that fails on duplicate operationIds with the offending file names."],"tags":["openapi","spec-merge","operationid","conflict","java"],"backgroundTag":"duplicate-operation-id","analyzedSha":"fcec517be3cf5b7964296bcba25fbc97541484e7","analyzedAt":"2026-08-22T11:13:11.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}