quarkusio/quarkus · error · IllegalArgumentException
No valueExpressionResolver is defined
Error message
No valueExpressionResolver is defined
What it means
When a @MeterTag parameter uses an expression (annotation.expression() is non-blank) and no resolver class is set, Quarkus's MeterTagsSupport needs a ValueExpressionResolver bean to evaluate the expression. That resolver is injected as Instance<ValueExpressionResolver>; when no such CDI bean exists in the application it is null and resolveTagValue throws IllegalArgumentException. This mirrors Micrometer's MeterTagAnnotationHandler behavior.
Source
Thrown at extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/MeterTagsSupport.java:75
private static Tags getCommonTags(ArcInvocationContext context) {
Method method = context.getMethod();
String className = method.getDeclaringClass().getName();
String methodName = method.getName();
return Tags.of("class", className, "method", methodName);
}
/*
* Precedence copied from MeterTagAnnotationHandler
*/
private String resolveTagValue(MeterTag annotation, Object parameterValue) {
String value = null;
if (annotation.resolver() != NoOpValueResolver.class) {
ValueResolver valueResolver = valueResolvers.get(annotation.resolver());
value = valueResolver.resolve(parameterValue);
} else if (StringUtils.isNotBlank(annotation.expression())) {
if (valueExpressionResolver == null) {
throw new IllegalArgumentException("No valueExpressionResolver is defined");
}
value = valueExpressionResolver.resolve(annotation.expression(), parameterValue);
} else if (parameterValue != null) {
value = parameterValue.toString();
}
return value == null ? "" : value;
}
/*
* Precedence copied from MeterTagAnnotationHandler
*/
private static String resolveTagKey(MeterTag annotation, String parameterName) {
if (StringUtils.isNotBlank(annotation.value())) {
return annotation.value();
} else if (StringUtils.isNotBlank(annotation.key())) {
return annotation.key();
} else {
return parameterName;View on GitHub (pinned to e1c734241f)
Solutions
- Provide a CDI bean implementing io.micrometer.common.annotation.ValueExpressionResolver, e.g. a @Singleton class that implements resolve(String expression, Object parameterValue)
- Alternatively switch the @MeterTag to use resolver = MyValueResolver.class instead of an expression, so the expression path is never taken
- Remove the expression from @MeterTag and rely on the default parameterValue.toString() rendering
- Verify the resolver bean is in a package scanned by Quarkus and properly annotated so Arc registers it
Example fix
// before
void metric(@MeterTag(expression = "upper") String region) { ... }
// throws: no ValueExpressionResolver
// after
@Singleton
public class UpperCaseValueExpressionResolver implements ValueExpressionResolver {
public String resolve(String expression, Object value) {
return value == null ? "" : value.toString().toUpperCase();
}
}
void metric(@MeterTag(expression = "upper") String region) { ... } Defensive patterns
Strategy: validation
Validate before calling
boolean hasExpressionResolver = CDI.current().select(ValueExpressionResolver.class).isResolvable();
if (!hasExpressionResolver && usesMeterTagExpressions()) {
throw new IllegalStateException("Define a ValueExpressionResolver bean to use @MeterTag expressions");
} Type guard
static boolean canResolveExpression(MeterTag tag) {
return tag.resolver() != NoOpValueResolver.class || tag.expression() == null || tag.expression().isBlank();
} Try / catch
try {
return invocation.proceed();
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("valueExpressionResolver")) {
log.error("@MeterTag expression used without a ValueExpressionResolver bean");
}
throw e;
} Prevention
- Always ship a @Singleton ValueExpressionResolver bean when you use @MeterTag expressions
- Prefer resolver = MyResolver.class on @MeterTag over string expressions
- Add a startup test that calls an @Timed method with @MeterTag params
When it happens
Trigger: Annotating a method parameter with @MeterTag(expression = "...") inside a @Timed/@Counted intercepted bean, while the application defines no bean implementing io.micrometer.common.annotation.ValueExpressionResolver and the annotation's resolver() is left at NoOpValueResolver.
Common situations: Copying Micrometer examples that use SpEL-style expressions (Spring provides one automatically) into a Quarkus app where you must supply your own ValueExpressionResolver bean; upgrading Quarkus/Micrometer where expression support became stricter; a resolver bean that is not annotated so Arc never discovers it.
Related errors
- While using @Transactional we need a DatabaseActions Strateg
- Multiple @Inject constructors on ${theClass}
- Quarkus does not support CDI Full @Specializes annotation; t
- IllegalStateException wrapping ClassNotFoundException for ge
- Unable to derive the logger name at ${injectionPoint}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b404fdc012a90a5e.
Report an issue: GitHub.