quarkusio/quarkus · error · IllegalArgumentException

Illegal placeholder in Maven CI friendly version property "$

Error message

Illegal placeholder in Maven CI friendly version property "${prop}": ${resolved}
	Please consult https://maven.apache.org/maven-ci-friendly.html#single-project-setup

What it means

ModelUtils.resolveVersion expands CI-friendly version properties (${revision}, ${sha1}, ${changelist}) using the model properties and system properties. If a resolved property value itself contains `${...}` (a nested/unresolved placeholder), the version would still not be concrete, so it throws IllegalArgumentException with a pointer to Maven's CI-friendly docs.

Source

Thrown at independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/workspace/ModelUtils.java:208

    public static boolean isUnresolvedVersion(String version) {
        return getUnresolvedVersionPattern().matcher(version).find();
    }

    public static String resolveVersion(String rawVersion, Model rawModel) {
        final Map<String, String> props = new HashMap<>(rawModel.getProperties().size() + System.getProperties().size());
        putAll(props, rawModel.getProperties());
        putAll(props, System.getProperties());

        Matcher matcher = getUnresolvedVersionPattern().matcher(rawVersion);
        StringBuilder sb = new StringBuilder();
        while (matcher.find()) {
            final String resolved = props.get(matcher.group(1));
            if (resolved == null) {
                return null;
            }
            if (resolved.contains("${")) {
                throw new IllegalArgumentException("Illegal placeholder in Maven CI friendly version property \""
                        + matcher.group(1) + "\": " + resolved
                        + "\n\tPlease consult https://maven.apache.org/maven-ci-friendly.html#single-project-setup");
            }
            matcher.appendReplacement(sb, resolved);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static void putAll(Map<String, String> map, Properties props) {
        for (Map.Entry<Object, Object> e : props.entrySet()) {
            map.put(e.getKey().toString(), e.getValue().toString());
        }
    }

    /**
     * If the model contains properties, this method overrides those that appear to be
     * defined as system properties.

View on GitHub (pinned to e1c734241f)

Solutions

  1. Run Maven with the property set explicitly, e.g. `mvn -Drevision=1.0.0 package` or export it as a system property so the bootstrap can resolve it.
  2. Replace the nested placeholder with a literal value in the pom.xml (e.g. `<revision>1.0.0-SNAPSHOT</revision>`).
  3. Use the Maven flatten plugin (flatten-maven-plugin with `resolveCiFriendliesOnly`) so the installed/parsed model contains a concrete version.
  4. Follow https://maven.apache.org/maven-ci-friendly.html#single-project-setup for a valid CI-friendly setup.

Example fix

<!-- before -->
<properties>
  <revision>${project.version}</revision>
</properties>
<version>${revision}</version>
<!-- after -->
<properties>
  <revision>1.0.0-SNAPSHOT</revision>
</properties>
<version>${revision}</version>
Defensive patterns

Strategy: validation

Validate before calling

String rev = model.getProperties().getProperty("revision");
if (model.getVersion() != null && model.getVersion().contains("${revision}")
        && (rev == null || rev.contains("${"))) {
    throw new IllegalArgumentException(
        "Set -Drevision=<value> or a literal <revision> property in the pom");
}

Type guard

static boolean hasResolvableCiFriendlyVersion(Model model) {
    String v = model.getVersion();
    if (v == null || !v.contains("${")) return true;
    for (String p : new String[]{"revision", "sha1", "changelist"}) {
        if (v.contains("${" + p + "}")) {
            String val = model.getProperties().getProperty(p);
            if (val == null || val.contains("${")) return false;
        }
    }
    return true;
}

Try / catch

try {
    String version = ModelUtils.getVersion(model);
} catch (IllegalArgumentException e) {
    // nested/unresolved CI-friendly placeholder; advise flattening or -D override
}

Prevention

When it happens

Trigger: A pom.xml with e.g. `<version>${revision}</version>` and `<revision>${another}</revision>` where `another` is not defined, or where a property value chains into another placeholder; calling resolveVersion/getVersion on such a raw model.

Common situations: Migrating to CI-friendly versions (`revision/changelist/sha1`) without flattening or passing `-Drevision=...`; defining the property via a parent POM not on disk or via settings.xml in a raw-model context; typos in property names causing fallback values containing placeholders; using CI-friendly versions in a Quarkus dev-mode app where Maven's flatten step is skipped.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/172b45326e4078ce. Report an issue: GitHub.