elastic/elasticsearch · error · IllegalArgumentException

Invalid version format

Error message

Invalid version format: '{s}'. Should be {pattern}

What it means

Thrown by QualifiedVersion.of when the supplied string does not match the strict semver pattern held in `pattern`. The matcher requires a precise major.minor.patch(+qualifier) shape; any deviation — missing component, extra segments, non-numeric parts — fails matcher.matches() and the task rejects the input with the expected pattern echoed in the message.

Solutions

  1. Reformat the version string to strict major.minor.patch with an optional qualifier that Qualifier.of accepts (e.g. '8.0.0', '8.0.0-alpha1', '8.0.0-rc1').
  2. Check the echoed `pattern` in the message for the exact accepted shape and align the input.
  3. If the input legitimately carries build metadata, parse/strip it before calling QualifiedVersion.of.
  4. Add a unit test for the version string to catch regressions in the producing tool.

Example fix

// before
QualifiedVersion.of("8.0");
QualifiedVersion.of("8.0.0.1");
// after
QualifiedVersion.of("8.0.0");
QualifiedVersion.of("8.0.1");
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern STRICT_SEMVER =
    Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\p{Alnum}+))?$");
if (STRICT_SEMVER.matcher(s).matches() == false) {
    throw new IllegalArgumentException("Not strict semver: " + s);
}

Type guard

static boolean isQualifiedVersion(String s) {
    return s != null && STRICT_SEMVER.matcher(s).matches();
}

Try / catch

try { QualifiedVersion v = QualifiedVersion.of(s); }
catch (IllegalArgumentException e) {
    // normalize s (strip build metadata) then retry, or surface to the user
    throw e;
}

Prevention

When it happens

Trigger: QualifiedVersion.of(s) is called with a string that isn't strict semver: missing patch (e.g. '8.0'), extra build metadata formatted wrong, a non-numeric component, leading zeros, or a qualifier that doesn't match Qualifier.of's accepted set. The pattern is matched before any parsing, so the failure is purely format validation.

Common situations: Passing a Maven-style version ('8.0.0-SNAPSHOT' may be valid, but '8.0' or '8.0.0.1' is not); a version string sourced from a git tag/env var that includes extra metadata; copy-paste error; mixing qualified and unqualified semver formats across release tooling.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/b07dab75ee231cf6. Report an issue: GitHub.

Appendix: source

Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/QualifiedVersion.java:43

 */
public record QualifiedVersion(int major, int minor, int revision, Qualifier qualifier) implements Comparable<QualifiedVersion> {

    private static final Pattern pattern = Pattern.compile(
        "^v? (\\d+) \\. (\\d+) \\. (\\d+) (?: - (alpha\\d+ | beta\\d+ | rc\\d+ | SNAPSHOT ) )? $",
        Pattern.COMMENTS
    );

    /**
     * Parses the supplied string into an object.
     *
     * @param s a version string in strict semver
     * @return a new instance
     */
    public static QualifiedVersion of(final String s) {
        Objects.requireNonNull(s);
        Matcher matcher = pattern.matcher(s);
        if (matcher.matches() == false) {
            throw new IllegalArgumentException("Invalid version format: '" + s + "'. Should be " + pattern);
        }

        return new QualifiedVersion(
            Integer.parseInt(matcher.group(1)),
            Integer.parseInt(matcher.group(2)),
            Integer.parseInt(matcher.group(3)),
            matcher.group(4) == null ? null : Qualifier.of(matcher.group(4))
        );
    }

    @Override
    public String toString() {
        return String.format(Locale.ROOT, "%d.%d.%d%s", major, minor, revision, qualifier == null ? "" : "-" + qualifier);
    }

    public boolean hasQualifier() {
        return qualifier != null;
    }

View on GitHub (pinned to db6a809a66)