quarkusio/quarkus · error · IllegalArgumentException

Cannot mix expressions

Error message

Cannot mix expressions

What it means

ClientHeaderParam values support one expression kind per position: ${config.property} for config lookups and {methodOrParam} for accessible/method references. Starting a ${...} expression while another config or accessible expression is still open means the value nests/mixes expression syntax, which the parser rejects with IllegalArgumentException at build time.

Source

Thrown at extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java:1176

        private final String sourceMethod;

        RestClientAnnotationExpressionParser(String input, String sourceMethod) {
            this.input = Objects.requireNonNull(input);
            this.sourceMethod = sourceMethod;
        }

        // this is a pretty naive implementation, but it suffices for what we are trying to do
        List<Node> parse() {
            int i = 0;
            int configStart = -1;
            int accessibleStart = -1;
            int verbatimStart = -1;
            List<Node> nodes = new ArrayList<>();
            while (i < input.length()) {
                char c = input.charAt(i);
                if (c == '$') {
                    if ((configStart != -1) || (accessibleStart != -1)) {
                        throw new IllegalArgumentException(createEffectiveErrorMessage("Cannot mix expressions"));
                    }
                    if (i == input.length() - 1) {
                        throw new IllegalArgumentException(createEffectiveErrorMessage("Illegal end of expression"));
                    }
                    if (input.charAt(i + 1) != '{') {
                        throw new IllegalArgumentException(createEffectiveErrorMessage("'$' must always be followed by '{'"));
                    }
                    if (verbatimStart != -1) {
                        nodes.add(new Verbatim(input.substring(verbatimStart, i)));
                    }
                    i += 2;
                    configStart = i;
                    verbatimStart = -1;
                } else if (c == '{') {
                    if ((configStart != -1) || (accessibleStart != -1)) {
                        throw new IllegalArgumentException(createEffectiveErrorMessage("Cannot mix expressions"));
                    }
                    if (i == input.length() - 1) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Close each expression with } before starting another (e.g. "{method}-${prop}" not "{method}${prop}" without closing braces)
  2. Remove the nested expression and use either a config lookup or a method reference, not both inside one expression
  3. If you need concatenation, use separate expressions separated by literal text: "{m}-${p}"

Example fix

// before
@ClientHeaderParam(name="X", value="${token{getId}}")

// after
@ClientHeaderParam(name="X", value="${token}-{getId}")
Defensive patterns

Strategy: validation

Validate before calling

// Reject nested/unclosed expressions before annotating
String v = "${token{getId}}";
int open = 0;
for (char c : v.toCharArray()) {
    if (c == '{') open++;
    if (c == '}') open--;
    if (open > 1) throw new IllegalArgumentException("Nested expressions not allowed in " + v);
}

Prevention

When it happens

Trigger: A value like "{methodA}${property}" where an accessible expression was opened and not closed before the '$', or nested/malformed braces such as "${a${b}}".

Common situations: Hand-editing annotation values to combine a config placeholder with a method call in one expression, or typos leaving an expression unclosed before a '$' appears.

Related errors


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