quarkusio/quarkus · error · IllegalArgumentException

Expresion command is reserved: " + command

Error message

Expresion command is reserved: " + command

What it means

Thrown by ParserConfig when the expression command character collides with a character already reserved by the Qute parser: any command character of a built-in Tag (via Parser.Tag.isCommand), the comment delimiter, the CDATA start delimiter, or underscore. Such a char would make template syntax ambiguous.

Source

Thrown at independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserConfig.java:30

 */
public record ParserConfig(Character expressionCommand) {

    /**
     * By default, no special expression command is used.
     */
    public static final ParserConfig DEFAULT = new ParserConfig(null);

    public ParserConfig {
        if (expressionCommand != null) {
            char command = expressionCommand.charValue();
            if (command < 0 || command > 127) {
                throw new IllegalArgumentException("Expresion command must be an ASCII char: " + command);
            }
            if (Parser.Tag.isCommand(command, null)
                    || command == Parser.COMMENT_DELIMITER
                    || command == Parser.CDATA_START_DELIMITER
                    || command == Parser.UNDERSCORE) {
                throw new IllegalArgumentException("Expresion command is reserved: " + command);
            }
            if (Character.isDigit(command)
                    || Character.isAlphabetic(command)) {
                throw new IllegalArgumentException("Expresion command must not be a digit/alphabetic: " + command);
            }
        }
    }

}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Pick a character not used by any Qute tag command, comment (!), CDATA ([), or underscore (_), e.g. '$'.
  2. Check the chosen char against Parser.Tag.isCommand(char, null) plus Parser.COMMENT_DELIMITER, CDATA_START_DELIMITER and UNDERSCORE before configuring.
  3. If you need different section delimiters too, configure them separately rather than overloading the expression command.

Example fix

// before
ParserConfig config = new ParserConfig('#'); // reserved: tag command

// after
ParserConfig config = new ParserConfig('$'); // free ASCII char
Defensive patterns

Strategy: validation

Validate before calling

char c = command;
boolean reserved = Parser.Tag.isCommand(c, null)
        || c == Parser.COMMENT_DELIMITER
        || c == Parser.CDATA_START_DELIMITER
        || c == Parser.UNDERSCORE;
if (reserved) {
    throw new IllegalArgumentException("expression command is reserved: " + c);
}

Try / catch

try {
    ParserConfig config = new ParserConfig(command);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("reserved")) {
        // pick a different, non-reserved char and retry configuration
    }
}

Prevention

When it happens

Trigger: Constructing ParserConfig with an expression command equal to e.g. '@' or '#' or '[' or '_' (characters used for other tag kinds), or the default '{' itself.

Common situations: Users trying to unify delimiters (e.g. wanting all tags to start with '#'), not realizing comment/CDATA/section commands already consume that character; blindly swapping '{' for another common symbol.

Related errors


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