elastic/elasticsearch · critical · IllegalStateException

Expected elasticsearch version to be numbers only of the for

Error message

Expected elasticsearch version to be numbers only of the form  X.Y.Z but it was: ${elasticsearch}

What it means

After confirming the elasticsearch property exists, the loader validates it against the strict regex [0-9]+\.[0-9]+\.[0-9]+ and rejects anything that is not a bare X.Y.Z. This prevents suffixes like -SNAPSHOT or -rc1 from leaking into the base version, since those are appended programmatically from the qualifier and build.snapshot sysprops.

Source

Thrown at build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesLoader.java:38

// Define this here because we need it early.
public class VersionPropertiesLoader {
    static Properties loadBuildSrcVersion(File input, ProviderFactory providerFactory) throws IOException {
        Properties props = new Properties();
        try (InputStream is = new FileInputStream(input)) {
            props.load(is);
        }
        loadBuildSrcVersion(props, providerFactory);
        return props;
    }

    protected static void loadBuildSrcVersion(Properties loadedProps, ProviderFactory providers) {
        String elasticsearch = loadedProps.getProperty("elasticsearch");
        if (elasticsearch == null) {
            throw new IllegalStateException("Elasticsearch version is missing from properties.");
        }
        if (elasticsearch.matches("[0-9]+\\.[0-9]+\\.[0-9]+") == false) {
            throw new IllegalStateException(
                    "Expected elasticsearch version to be numbers only of the form  X.Y.Z but it was: " +
                            elasticsearch
            );
        }
        String qualifier = providers.systemProperty("build.version_qualifier")
                .getOrElse("");
        if (qualifier.isEmpty() == false) {
            if (qualifier.matches("(alpha|beta|rc)\\d+") == false) {
                throw new IllegalStateException("Invalid qualifier: " + qualifier);
            }
            elasticsearch += "-" + qualifier;
        }
        final String buildSnapshotSystemProperty = providers.systemProperty("build.snapshot")
                .getOrElse("true");
        switch (buildSnapshotSystemProperty) {
            case "true":
                elasticsearch += "-SNAPSHOT";
                break;

View on GitHub (pinned to db6a809a66)

Solutions

  1. Edit version.properties so elasticsearch is a bare X.Y.Z, e.g. `elasticsearch=9.0.0`.
  2. Remove any -SNAPSHOT, -rc1, or v prefix from the property; pass those via -Dbuild.version_qualifier and -Dbuild.snapshot instead.
  3. Re-generate the file from the release tooling rather than editing it by hand.

Example fix

// before
elasticsearch=9.0.0-SNAPSHOT
// after
elasticsearch=9.0.0
Defensive patterns

Strategy: validation

Validate before calling

String v = props.getProperty("elasticsearch");
if (v == null || !v.matches("[0-9]+\\.[0-9]+\\.[0-9]+")) {
    throw new GradleException("elasticsearch version must be bare X.Y.Z, got: " + v);
}

Prevention

When it happens

Trigger: loadBuildSrcVersion sees an elasticsearch value such as 9.0.0-SNAPSHOT, 9.0, v9.0.0, or 9.0.0.1 — anything not matching exactly three dot-separated numeric groups.

Common situations: Someone hand-edited version.properties to include the snapshot suffix; a release script wrote a full qualified version into the base property; a branch merge introduced a malformed version string; a downstream fork pinned a two-component version.

Related errors


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