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

  1. Provide a CDI bean implementing io.micrometer.common.annotation.ValueExpressionResolver, e.g. a @Singleton class that implements resolve(String expression, Object parameterValue)
  2. Alternatively switch the @MeterTag to use resolver = MyValueResolver.class instead of an expression, so the expression path is never taken
  3. Remove the expression from @MeterTag and rely on the default parameterValue.toString() rendering
  4. 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

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


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