{"record":{"id":"719874471c5bd73e","repo":"quarkusio/quarkus","slug":"replacement-id-must-be-in-range-0-15-but-was","errorCode":null,"errorMessage":"Replacement ID must be in range [0, 15] but was: ","messagePattern":"Replacement ID must be in range \\[0, 15\\] but was: ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"independent-projects/qute/core/src/main/java/io/quarkus/qute/HtmlEscaper.java","lineNumber":40,"sourceCode":"        setLatinReplacementId(data, '\"', 1);\n        setLatinReplacementId(data, '\\'', 2);\n        setLatinReplacementId(data, '&', 3);\n        setLatinReplacementId(data, '<', 4);\n        setLatinReplacementId(data, '>', 5);\n        assert getLatinReplacementId(data, '\"') == 1;\n        assert getLatinReplacementId(data, '\\'') == 2;\n        assert getLatinReplacementId(data, '&') == 3;\n        assert getLatinReplacementId(data, '<') == 4;\n        assert getLatinReplacementId(data, '>') == 5;\n        return data;\n    }\n\n    private static void setLatinReplacementId(byte[] data, int c, int id) {\n        if (c > 255) {\n            throw new IllegalArgumentException(\"Only Latin characters are supported: \" + c);\n        }\n        if (id < 0 || id > 15) {\n            throw new IllegalArgumentException(\"Replacement ID must be in range [0, 15] but was: \" + id);\n        }\n        data[c] = (byte) id;\n    }\n\n    private static int getLatinReplacementId(byte[] data, int c) {\n        return data[c] & REPLACEMENT_ID_MASK;\n    }\n\n    private static String replacementOf(char c) {\n        if (c > 255) {\n            return null;\n        }\n        int replacementId = getLatinReplacementId(LATIN_REPLACEMENT_ID_TABLE, c & 0xFF);\n        // in the super class we still have to perform a null check vs String, which means\n        // we can have a branch misprediction there.\n        // Here we anticipate such cost and if this method is going to be inlined we could still\n        // correctly predict if the subsequent null check is going to be taken or not.\n        if (replacementId == 0) {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/qute/core/src/main/java/io/quarkus/qute/HtmlEscaper.java#L22-L58","documentation":"The HtmlEscaper replacement table stores 4-bit replacement ids in each byte; setLatinReplacementId() validates that the id is in [0, 15] and throws IllegalArgumentException otherwise. It is an internal invariant check while building the escape table.","triggerScenarios":"Passing an escape-replacement id < 0 or > 15 to setLatinReplacementId — only possible when modifying/extending createLatinReplacementData with too many distinct replacements.","commonSituations":"Patching HtmlEscaper to add more escape characters than the 4-bit encoding supports (max 16 distinct replacement ids).","solutions":["Keep the number of distinct replacement ids at or below 16 (ids 0-15).","Widen the encoding (e.g. use short[] instead of byte[]) if more replacements are required.","Deduplicate identical replacement strings to share ids."],"exampleFix":"// before\nsetLatinReplacementId(data, '\"', 16); // out of range -> throws\n// after\nsetLatinReplacementId(data, '\"', 7); // reuse an id within [0, 15], or dedupe replacements","handlingStrategy":"validation","validationCode":"if (id < 0 || id > 15) {\n    throw new IllegalArgumentException(\"replacement id must fit in 4 bits\");\n}","typeGuard":"boolean isValidReplacementId(int id) { return id >= 0 && id <= 15; }","tryCatchPattern":"try {\n    setLatinReplacementId(data, c, id);\n} catch (IllegalArgumentException ex) {\n    LOGGER.error(\"too many distinct replacements in escape table\");\n    throw ex;\n}","preventionTips":["Keep at most 16 distinct replacement ids in the escape table","Deduplicate identical replacement strings","Widen storage (short[]/int[]) if more replacements are ever needed"],"tags":["qute","html-escaping","illegal-argument","range-check"],"backgroundTag":"argument-out-of-range","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}