apache/maven · error · ExpressionEvaluationException
Error evaluating plugin parameter expression: ${expression}
Error message
Error evaluating plugin parameter expression: ${expression} What it means
The Maven 4 evaluator (PluginParameterExpressionEvaluatorV4) resolves the expression against the consumer-POM model context (ctx.getValue(), typically the org.apache.maven.api Project or Session) and that reflective evaluation threw. It is the same ExpressionEvaluationException family, but rooted in the new consumer-API objects whose getter surface differs from the legacy MavenProject/Settings.
Source
Thrown at impl/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluatorV4.java:161
try {
int pathSeparator = expression.indexOf('/');
if (pathSeparator > 0) {
String pathExpression = expression.substring(0, pathSeparator);
value = ReflectionValueExtractor.evaluate(pathExpression, ctx.getValue());
if (pathSeparator < expression.length() - 1) {
if (value instanceof Path path) {
value = path.resolve(expression.substring(pathSeparator + 1));
} else {
value = value + expression.substring(pathSeparator);
}
}
} else {
value = ReflectionValueExtractor.evaluate(expression, ctx.getValue());
}
break;
} catch (Exception e) {
// TODO don't catch exception
throw new ExpressionEvaluationException(
"Error evaluating plugin parameter expression: " + expression, e);
}
}
}
/*
* MNG-4312: We neither have reserved all of the above magic expressions nor is their set fixed/well-known (it
* gets occasionally extended by newer Maven versions). This imposes the risk for existing plugins to
* unintentionally use such a magic expression for an ordinary property. So here we check whether we
* ended up with a magic value that is not compatible with the type of the configured mojo parameter (a string
* could still be converted by the configurator so we leave those alone). If so, back off to evaluating the
* expression from properties only.
*/
if (value != null && type != null && !(value instanceof String) && !isTypeCompatible(type, value)) {
value = null;
}
if (value == null) {View on GitHub (pinned to e4093d4e12)
Solutions
- Check the path against the org.apache.maven.api model interfaces (Project, Session, Settings) rather than the legacy classes.
- Move custom data you need in expressions into user properties (-D) or <properties>, which survive consumer-POM processing.
- Test with mvn help:evaluate -DforceStdout under the same Maven major version you build with.
- Avoid deep chains; reference only first- or second-level getters.
Example fix
<!-- before: legacy-only path, absent on the Maven 4 consumer Project -->
<value>${project.model.build}</value>
<!-- after: use the consumer-model surface or a property -->
<value>${project.build.directory}</value> Defensive patterns
Strategy: validation
Validate before calling
mvn -q org.apache.maven.plugins:maven-help-plugin:3.4.1:evaluate \ -Dexpression=project.build.directory -DforceStdout || echo 'BAD EXPRESSION'
Try / catch
try {
v = evaluatorV4.evaluate(expr, String.class);
} catch (ExpressionEvaluationException e) {
// fall back to a plain user property so builds stay reproducible across model versions
v = session.getUserProperties().getProperty(expr.replaceAll("[^a-zA-Z0-9.]", ""));
} Prevention
- Check expressions against org.apache.maven.api model interfaces, not legacy MavenProject
- Move data you reference in expressions into user properties to survive consumer-POM processing
- Test POMs with mvn help:evaluate under the same Maven major version used in CI
When it happens
Trigger: An expression that resolves on the legacy model but points at a property missing on the v4 consumer model; a nested path traversing a null intermediate on the v4 object graph; expressions referencing build-time-only data stripped from the consumer POM.
Common situations: Plugins migrated to the Maven 4 API; builds where the consumer POM no longer carries elements (distribution, development ids) that the expression expects; mixed 3.x/4.x expression behavior.
Related errors
- Error evaluating plugin parameter expression: ${expression}
- Unable to inject field '${resolution.getField()}' annotated
- Failed to interpolate field: " + field + " on class: " + cls
- The token '%s' at position '%d' refers to a java.util.Map, b
- The token '%s' at position '%d' refers to a java.util.List o
AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21).
Data as JSON: /api/errors/ab2b24596cea5d26.
Report an issue: GitHub.