quarkusio/quarkus · error · IllegalArgumentException

Only Latin characters are supported:

Error message

Only Latin characters are supported: 

What it means

HtmlEscaper's replacement table maps byte values 0-255 to escape-replacement ids. setLatinReplacementId() throws IllegalArgumentException when asked to register a character code greater than 255, because the table only covers Latin-1 characters. This guards the static table-building code from non-Latin characters.

Source

Thrown at independent-projects/qute/core/src/main/java/io/quarkus/qute/HtmlEscaper.java:37

    private static byte[] createLatinReplacementData() {
        byte[] data = new byte[256];
        // by default we don't escape anything i.e. the escaped index is 0!
        setLatinReplacementId(data, '"', 1);
        setLatinReplacementId(data, '\'', 2);
        setLatinReplacementId(data, '&', 3);
        setLatinReplacementId(data, '<', 4);
        setLatinReplacementId(data, '>', 5);
        assert getLatinReplacementId(data, '"') == 1;
        assert getLatinReplacementId(data, '\'') == 2;
        assert getLatinReplacementId(data, '&') == 3;
        assert getLatinReplacementId(data, '<') == 4;
        assert getLatinReplacementId(data, '>') == 5;
        return data;
    }

    private static void setLatinReplacementId(byte[] data, int c, int id) {
        if (c > 255) {
            throw new IllegalArgumentException("Only Latin characters are supported: " + c);
        }
        if (id < 0 || id > 15) {
            throw new IllegalArgumentException("Replacement ID must be in range [0, 15] but was: " + id);
        }
        data[c] = (byte) id;
    }

    private static int getLatinReplacementId(byte[] data, int c) {
        return data[c] & REPLACEMENT_ID_MASK;
    }

    private static String replacementOf(char c) {
        if (c > 255) {
            return null;
        }
        int replacementId = getLatinReplacementId(LATIN_REPLACEMENT_ID_TABLE, c & 0xFF);
        // in the super class we still have to perform a null check vs String, which means
        // we can have a branch misprediction there.

View on GitHub (pinned to e1c734241f)

Solutions

  1. Only register escape replacements for characters with code point <= 255 in the table.
  2. Handle non-Latin characters with a separate escaping mechanism (e.g. a map checked before the byte table).
  3. If this fires unexpectedly, audit any local modifications to createLatinReplacementData for bad character constants.

Example fix

// before
setLatinReplacementId(data, '\u2014', 12); // em dash > 255 -> throws
// after
if ('\u2014' <= 255) {
    setLatinReplacementId(data, '\u2014', 12);
} // handle em dash elsewhere, e.g. in a separate unicode escape map
Defensive patterns

Strategy: validation

Validate before calling

if (codePoint > 255) {
    // route to a separate unicode escaping path, not the Latin-1 byte table
}

Type guard

boolean isLatin1(int c) { return c >= 0 && c <= 255; }

Try / catch

try {
    setLatinReplacementId(data, c, id);
} catch (IllegalArgumentException ex) {
    LOGGER.error("non-Latin escape char in table: {}", ex.getMessage());
    throw ex;
}

Prevention

When it happens

Trigger: Internal configuration/build of the escape table with a character code > 255 — practically only reachable if createLatinReplacementData is modified or a caller registers a non-Latin (e.g. Unicode) character as a replacement target.

Common situations: Contributing a patch or customizing HtmlEscaper to add extra escape characters and using a code point beyond 255 (e.g. typographic quotes, em dash).

Related errors


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