OpenAPITools/openapi-generator · error · RuntimeException

Cannot merge specs where some declare an OpenAPI version…

Error message

Cannot merge specs where some declare an OpenAPI version and others do not. The 'openapi' field is required; every source spec must declare a version so compatibility can be verified.

What it means

MergedSpecBuilder.resolveOutputVersion() collects each source spec's `openapi` field. If at least one spec declares a version and at least one does not, merging is refused: an unversioned source cannot be assumed compatible with a versioned one, and the code refuses to guess. Only when every source is unversioned does it fall back to the default '3.0.3'. This is a deliberate strictness change — the required-ness of `openapi` is enforced at merge time.

Solutions

  1. Add an explicit `openapi: <version>` field to every source spec in the merge set, matching the others' major.minor.
  2. Alternatively remove the field from all sources so the 3.0.3 default applies — only valid if the specs are genuinely 3.0.x-compatible.
  3. Validate with a pre-merge lint rule that every file has an `openapi` key.

Example fix

# before (fragments.yaml)
components:
  parameters: ...
# after
openapi: 3.0.3
components:
  parameters: ...
Defensive patterns

Strategy: validation

Validate before calling

// Every spec must declare an openapi version before merge
for (String f : specFiles) {
    JsonNode n = new ObjectMapper(new YAMLFactory()).readTree(new File(f));
    if (n.get("openapi") == null || n.get("openapi").asText().isBlank()) {
        throw new IllegalArgumentException(f + " is missing the required 'openapi' field");
    }
}

Try / catch

Catch RuntimeException with "Cannot merge specs where some declare"; list which inputs lack the openapi field and stop for manual fix.

Prevention

When it happens

Trigger: Merging pet.yaml (openapi: 3.0.1) with fragments.yaml (no openapi field, e.g. a shared parameters/components file); adding a new component-only file without a version header to an existing versioned catalog.

Common situations: Spec catalogs where common fragments were never given `openapi` because individual generators tolerated it; converting multi-file Swagger 2.0-era catalogs; teams introducing new partial specs after a merge pipeline already exists.

Related errors


AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22). Data as JSON: /api/errors/e19173cb41b0ca80. Report an issue: GitHub.

Appendix: source

Thrown at modules/openapi-generator/src/main/java/org/openapitools/codegen/config/MergedSpecBuilder.java:528

     * unversioned. Malformed version strings (non-numeric segments such as {@code 3.x.3}) are
     * rejected outright.</p>
     *
     * @throws RuntimeException if the sources declare different major.minor OpenAPI versions, mix
     *                          versioned and unversioned specs, or contain a malformed version
     */
    private String resolveOutputVersion(List<OpenAPI> specs) {
        List<String> versions = specs.stream()
                .map(OpenAPI::getOpenapi)
                .collect(Collectors.toList());

        if (versions.stream().noneMatch(Objects::nonNull)) {
            // Every source is unversioned — fall back to the default.
            return "3.0.3";
        }
        // At least one source declares a version. Since 'openapi' is required, an unversioned
        // source cannot be assumed compatible and must not be silently accepted.
        if (versions.stream().anyMatch(Objects::isNull)) {
            throw new RuntimeException(
                    "Cannot merge specs where some declare an OpenAPI version and others do not. The 'openapi' "
                            + "field is required; every source spec must declare a version so compatibility can be verified.");
        }
        // Reject malformed versions before comparing, so a value like '3.x.3' cannot pass the
        // major.minor check by having its non-numeric segment normalized to zero.
        for (String version : versions) {
            if (!isWellFormedVersion(version)) {
                throw new RuntimeException(String.format(Locale.ROOT,
                        "Malformed OpenAPI version '%s' in a source spec. Expected exactly three numeric "
                                + "components (MAJOR.MINOR.PATCH) without leading zeroes, such as '3.0.3' or '3.1.0'.",
                        version));
            }
        }
        String firstMajorMinor = majorMinor(versions.get(0));
        for (String version : versions) {
            if (!majorMinor(version).equals(firstMajorMinor)) {
                throw new RuntimeException(String.format(Locale.ROOT,
                        "Cannot merge specs that declare incompatible OpenAPI versions %s. All specs must share "

View on GitHub (pinned to fcec517be3)