quarkusio/quarkus · error · IllegalArgumentException

'$' must always be followed by '{'

Error message

'$' must always be followed by '{'

What it means

In ClientHeaderParam values, '$' only introduces configuration expressions in the ${property} form. A '$' followed by any character other than '{' (e.g. $token) is rejected at build time with IllegalArgumentException, since bare $name syntax is not supported.

Source

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

        // 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) {
                        throw new IllegalArgumentException(createEffectiveErrorMessage("Illegal end of expression"));
                    }
                    if (verbatimStart != -1) {
                        nodes.add(new Verbatim(input.substring(verbatimStart, i)));
                    }
                    i++;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Use the full ${property} syntax: "$value" becomes "${value}"
  2. Remove the '$' if it was meant as literal text
  3. Move dollar-containing literal text so '$' is never directly followed by a non-'{' character, or use a method reference to produce the value

Example fix

// before
@ClientHeaderParam(name="X-Build", value="$buildId")

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

Strategy: validation

Validate before calling

String v = "$buildId";
for (int i = 0; i < v.length(); i++) {
    if (v.charAt(i) == '$' && (i == v.length() - 1 || v.charAt(i + 1) != '{')) {
        throw new IllegalArgumentException("'$' must be followed by '{': " + v);
    }
}

Prevention

When it happens

Trigger: Values like "$version" or "prefix-$value" inside @ClientHeaderParam — a '$' not immediately followed by '{'.

Common situations: Habit from Spring/property-placeholder syntax ($key instead of ${key}), or template strings containing currency amounts like "$100".

Related errors


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