OpenAPITools/openapi-generator · error · RuntimeException

Cannot merge specs where some declare an OpenAPI version and

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.

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)

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.