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
- Close each expression with } before starting another (e.g. "{method}-${prop}" not "{method}${prop}" without closing braces)
- Remove the nested expression and use either a config lookup or a method reference, not both inside one expression
- 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
- Close every expression before starting the next
- Concatenate expressions with literal separators instead of nesting
- Keep annotation values simple; compose complex values in Java code
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
- Illegal end of expression
- '$' must always be followed by '{'
- Method ${class}#${method} has an unsupported return type for
- Method ${class}#${method} has an unsupported return type for
- 'quarkus-narayana-lra' can only work if 'quarkus-rest-client
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/957bf82d1dbd32d3.
Report an issue: GitHub.