{"record":{"id":"d7953ec5eb95f5db","repo":"alibaba/nacos","slug":"illegal-hex-at-index-i","errorCode":null,"errorMessage":"illegal hex at index {i}","messagePattern":"illegal hex at index (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/NacosAiConfigKeyCodec.java","lineNumber":199,"sourceCode":"        StringBuilder sb = new StringBuilder(bytes.length * 2);\n        for (byte b : bytes) {\n            sb.append(Character.forDigit((b >> 4) & 0xF, 16));\n            sb.append(Character.forDigit(b & 0xF, 16));\n        }\n        return sb.toString();\n    }\n    \n    private static byte[] fromHex(String hex) {\n        int len = hex.length();\n        if ((len & 1) != 0) {\n            throw new IllegalArgumentException(\"illegal hex length: \" + len);\n        }\n        byte[] out = new byte[len / 2];\n        for (int i = 0; i < len; i += 2) {\n            int hi = Character.digit(hex.charAt(i), 16);\n            int lo = Character.digit(hex.charAt(i + 1), 16);\n            if (hi < 0 || lo < 0) {\n                throw new IllegalArgumentException(\"illegal hex at index \" + i);\n            }\n            out[i / 2] = (byte) ((hi << 4) + lo);\n        }\n        return out;\n    }\n    \n    private static boolean hasReservedEncodedPrefix(String value) {\n        return value.regionMatches(true, 0, ENCODED_PREFIX, 0, ENCODED_PREFIX.length());\n    }\n    \n    private static String fitToLength(String candidate, int maxLength, String preservedPrefix) {\n        if (candidate == null) {\n            return null;\n        }\n        String prefix = preservedPrefix == null ? \"\" : preservedPrefix;\n        if (candidate.length() <= maxLength && !isHashedPhysicalKey(candidate, prefix)) {\n            return candidate;\n        }","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/NacosAiConfigKeyCodec.java#L181-L217","documentation":"NacosAiConfigKeyCodec.fromHex encountered a character at position i that is not a valid hexadecimal digit (0-9, a-f, A-F). The encoder only produces lowercase hex, so any non-hex character indicates corruption or external tampering of the encoded segment.","triggerScenarios":"An encoded segment contains uppercase letters outside A-F, punctuation, or non-ASCII characters. A URL-decode or character-set conversion mangled the hex string. External system injected unexpected characters into the config key.","commonSituations":"URL-encoding/decoding applied to a segment that was not designed for URLs. Locale-specific case folding in a database collation. Copy-paste from a rich-text source that inserted invisible characters.","solutions":["Re-encode the original value with encodeSegment to regenerate clean lowercase hex.","Sanitize the segment: strip non-hex characters or reject if any are found before decoding.","Trace the segment through every transport/storage layer to find where the invalid character was introduced.","Ensure the storage layer preserves exact ASCII bytes (use a binary-safe collation)."],"exampleFix":"// before\nString bad = \"enc.0xGH\"; // G, H are not hex\nNacosAiConfigKeyCodec.decodeSegment(bad);\n\n// after\nif (!bad.substring(4).matches(\"[0-9a-fA-F]+\")) {\n    bad = NacosAiConfigKeyCodec.encodeSegment(originalValue);\n}\nNacosAiConfigKeyCodec.decodeSegment(bad);","handlingStrategy":"validation","validationCode":"public static boolean isCleanHex(String hex) {\n    if (hex == null || hex.length() % 2 != 0) return false;\n    for (int i = 0; i < hex.length(); i++) {\n        if (Character.digit(hex.charAt(i), 16) < 0) return false;\n    }\n    return true;\n}","typeGuard":"public static boolean isDecodableSegment(String s) {\n    if (s == null || s.isEmpty()) return true;\n    if (!s.startsWith(NacosAiConfigKeyCodec.ENCODED_PREFIX)) return true;\n    String hex = s.substring(NacosAiConfigKeyCodec.ENCODED_PREFIX.length());\n    return isCleanHex(hex) && !hex.isEmpty();\n}","tryCatchPattern":"try {\n    decoded = NacosAiConfigKeyCodec.decodeSegment(segment);\n} catch (IllegalArgumentException e) {\n    logger.error(\"Corrupted encoded segment with non-hex char: {}\", segment);\n    decoded = NacosAiConfigKeyCodec.encodeSegment(originalValue);\n}","preventionTips":["Use binary-safe storage collations to prevent character mangling.","Avoid URL-encoding/decoding pass-through on encoded segments.","Sanitize external input before it reaches the codec."],"tags":["codec","config-key","validation","data-corruption","hex"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}