{"record":{"id":"02f6a7648449de3b","repo":"alibaba/nacos","slug":"illegal-hex-length-len","errorCode":null,"errorMessage":"illegal hex length: {len}","messagePattern":"illegal hex length: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/NacosAiConfigKeyCodec.java","lineNumber":192,"sourceCode":"        if (hex.isEmpty()) {\n            throw new IllegalArgumentException(\"empty payload after \" + ENCODED_PREFIX);\n        }\n        return new String(fromHex(hex), StandardCharsets.UTF_8);\n    }\n    \n    private static String toHex(byte[] bytes) {\n        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) {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/NacosAiConfigKeyCodec.java#L174-L210","documentation":"NacosAiConfigKeyCodec.fromHex received a hex string whose length is odd. Every byte requires two hex characters, so an odd-length hex payload is structurally invalid and cannot be decoded into bytes.","triggerScenarios":"An encoded segment ('enc.' + hex) was truncated by one character. A custom or external process generated a partial hex string. Character encoding mismatch or copy-paste dropped a trailing nibble.","commonSituations":"Database column truncation (e.g. VARCHAR length off by one). Manual editing of a stored config key. A bug in a serialization layer that strips whitespace or control characters from hex output.","solutions":["Re-encode the original logical value with encodeSegment to produce a correct even-length hex string.","Inspect the stored segment byte-by-byte to find where truncation occurred.","Add a pre-check: if (hex.length() % 2 != 0) reject or re-encode before calling decodeSegment.","Check the database schema column length against the maximum encoded segment size."],"exampleFix":"// before\nString bad = \"enc.0a1\"; // odd length\nNacosAiConfigKeyCodec.decodeSegment(bad);\n\n// after -- re-encode from source of truth\nString good = NacosAiConfigKeyCodec.encodeSegment(originalValue);\nNacosAiConfigKeyCodec.decodeSegment(good);","handlingStrategy":"validation","validationCode":"public static boolean isValidHex(String hex) {\n    return hex != null && hex.length() % 2 == 0 && hex.matches(\"[0-9a-fA-F]+\");\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 !hex.isEmpty() && hex.length() % 2 == 0 && hex.matches(\"[0-9a-fA-F]+\");\n}","tryCatchPattern":"try {\n    String decoded = NacosAiConfigKeyCodec.decodeSegment(segment);\n} catch (IllegalArgumentException e) {\n    // segment is corrupted; re-encode from canonical source or skip\n    decoded = reEncodeFromSource(segment);\n}","preventionTips":["Re-encode from the original logical value rather than repairing hex by hand.","Ensure storage columns have sufficient length for encoded segments.","Validate encoded segments after any data migration."],"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"}