quarkusio/quarkus · error · IllegalArgumentException

Unexpected character '${str.charAt(pos)}' at position ${pos}

Error message

Unexpected character '${str.charAt(pos)}' at position ${pos}: ${str}

What it means

peekToken() throws this IllegalArgumentException when it encounters a character that cannot begin or continue a valid token in a Java type signature at the given position. It is the sibling of the 'Unexpected end of input' error and includes the offending character, its position, and the full string to make the malformed input easy to locate.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/types/TypeParser.java:221

        // token is a keyword or fully qualified name
        int begin = pos;
        while (pos < str.length() && Character.isJavaIdentifierStart(str.charAt(pos))) {
            do {
                pos++;
            } while (pos < str.length() && Character.isJavaIdentifierPart(str.charAt(pos)));

            if (pos < str.length() && str.charAt(pos) == '.') {
                pos++;
            } else {
                return str.substring(begin, pos);
            }
        }

        if (pos == str.length()) {
            throw new IllegalArgumentException("Unexpected end of input: " + str);
        }
        throw new IllegalArgumentException("Unexpected character '" + str.charAt(pos) + "' at position " + pos + ": " + str);
    }

    private String nextToken() {
        String result = peekToken();
        pos += result.length();
        return result;
    }

    private boolean isSpecial(char c) {
        return c == ',' || c == '?' || c == '<' || c == '>' || c == '[' || c == ']';
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Read the position reported in the message and inspect that character in the string
  2. Remove or replace the illegal character; use fully qualified binary names for reference types
  3. Do not pass JVM descriptor syntax ('Ljava/lang/String;') where a source-like name ('java.lang.String') is expected
  4. Pre-validate the signature with a regex/checksum or unit test before parsing

Example fix

// before
new TypeParser("Ljava/util/List;").parse(); // Unexpected character ';' at position 14
// after
new TypeParser("java.util.List").parse();
Defensive patterns

Strategy: validation

Validate before calling

boolean isSourceStyleTypeName(String s) {
    return s != null && s.matches("[\\w.$]+(<[\\s\\S]+>)?(\\[\\])*");
}

Type guard

// convert JVM descriptor form to source-style before parsing
String normalizeToSourceName(String s) {
    if (s != null && s.startsWith("L") && s.endsWith(";")) {
        return s.substring(1, s.length() - 1).replace('/', '.');
    }
    return s;
}

Try / catch

try {
    new TypeParser(sig).parse();
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Unexpected character")) {
        int pos = Integer.parseInt(e.getMessage().replaceFirst(".*position (\\d+):.*", "$1"));
        throw new IllegalArgumentException("Illegal char at index " + pos + " in " + sig, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Parsing strings containing characters outside the token alphabet — e.g. 'java.util.List<String>' (unqualified generic name with a bare 'S' is fine, but 'java.util.List@' or 'com.Foo;extra' contain characters the lexer does not accept), stray semicolons, commas in wrong places, or ASCII artifacts from copy/paste.

Common situations: Users pasting descriptor-style or internal names (e.g. 'Ljava/util/List;' with semicolons) into APIs that expect source-style names; typos in configured class names; strings containing whitespace or control characters not handled by the tokenizer.

Related errors


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