OpenAPITools/openapi-generator · error · RuntimeException

Malformed OpenAPI version '%s' in a source spec. Expected ex

Error message

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'.

What it means

Before comparing versions, resolveOutputVersion() runs every declared `openapi` value through isWellFormedVersion(), which demands exactly three numeric components (MAJOR.MINOR.PATCH) with no leading zeroes — e.g. '3.0.3' or '3.1.0'. This blocks malformed values like '3.x.3', '3.0', or '3.01.0' from sneaking through a lenient numeric parse that would normalize junk segments to zero. The malformed value is echoed in the message.

Source

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

                .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 "
                                + "the same major.minor version (e.g. all 3.0.x or all 3.1.x); mixing 3.0 and 3.1 "
                                + "is not supported because version-specific fields and semantics are not translated.",
                        versions.stream().distinct().collect(Collectors.toList())));
            }
        }
        // Same major.minor across all specs — declare the highest patch version encountered.
        String highest = versions.get(0);
        for (String version : versions) {

View on GitHub (pinned to fcec517be3)

Solutions

  1. Set the version in the flagged spec to a strict MAJOR.MINOR.PATCH form matching the other sources (e.g. 3.0.3).
  2. Remove leading zeroes from any segment (3.01.0 → 3.1.0).
  3. Add a CI lint (regex ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$) over all specs entering the merge.

Example fix

# before
openapi: 3.1
# after
openapi: 3.1.0
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern OAS_VERSION = Pattern.compile("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$");
for (String f : specFiles) {
    String v = readOpenapiField(f);
    if (v != null && !OAS_VERSION.matcher(v).matches()) {
        throw new IllegalArgumentException(f + ": malformed openapi version " + v);
    }
}

Try / catch

Catch RuntimeException containing "Malformed OpenAPI version"; echo the file + version and block the merge until fixed (deterministic config error).

Prevention

When it happens

Trigger: A source spec with `openapi: 3` or `openapi: 3.1`; a value like `openapi: 3.x.3` from hand-editing; leading-zero forms like `openapi: 3.0.01`.

Common situations: Hand-written partial specs where the version was abbreviated; templating that injects a placeholder into the version; tools emitting non-standard version strings; copy/paste from docs showing '3.x' wildcards.

Understand the failure class

Related errors


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