{"record":{"id":"9bf6d5beeb4fcb26","repo":"dromara/Sa-Token","slug":"hex-string-must-have-even-length","errorCode":null,"errorMessage":"Hex string must have even length","messagePattern":"Hex string must have even length","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sa-token-core/src/main/java/cn/dev33/satoken/util/SaHexUtil.java","lineNumber":55,"sourceCode":"        for (int i = 0; i < bytes.length; i++) {\n            int v = bytes[i] & 0xFF;\n            hexChars[i * 2] = HEX_ARRAY[v >>> 4];\n            hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];\n        }\n        return new String(hexChars);\n    }\n\n    /**\n     * 将十六进制字符串转换为字节数组（JDK8兼容）\n     * @param hexString 有效的十六进制字符串（不区分大小写）\n     * @return 对应的字节数组\n     * @throws IllegalArgumentException 输入字符串格式错误时抛出异常\n     */\n    public static byte[] hexToBytes(String hexString) {\n        if (hexString == null) return null;\n        int len = hexString.length();\n        if (len % 2 != 0) {\n            throw new IllegalArgumentException(\"Hex string must have even length\");\n        }\n\n        byte[] data = new byte[len / 2];\n        for (int i = 0; i < len; i += 2) {\n            int high = Character.digit(hexString.charAt(i), 16);\n            int low = Character.digit(hexString.charAt(i+1), 16);\n\n            if (high == -1 || low == -1) {\n                throw new IllegalArgumentException(\n                        \"Invalid hex character at position \" + i + \" or \" + (i+1)\n                );\n            }\n\n            data[i/2] = (byte) ((high << 4) + low);\n        }\n        return data;\n    }\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-core/src/main/java/cn/dev33/satoken/util/SaHexUtil.java#L37-L73","documentation":"SaHexUtil.hexToBytes converts a hexadecimal string to a byte array by consuming two hex chars per byte. An odd-length input cannot be split into byte pairs, so it fails fast with IllegalArgumentException('Hex string must have even length') before any parsing. This is a pure input-validation error on the caller's data.","triggerScenarios":"Calling SaHexUtil.hexToBytes(\"abc\") (3 chars), \"0\" , or any hex string with odd length; commonly the result of truncating a digest by hand, off-by-one slicing, or losing a character during copy/paste.","commonSituations":"Copying MD5/SHA hex digests and dropping the last char; substring-based truncation logic (e.g. token.substring(0, 31)); concatenating hex fragments where one piece had a leading zero stripped by a numeric conversion.","solutions":["Check str.length() % 2 == 0 before calling, and pad with a leading '0' when needed (String.format(\"%0\"+ (len+1) +\"d\"...) style fix or '0'+hex)","Avoid passing hex through numeric types (BigInteger.toString() drops leading zeros) — keep it as a String end-to-end","Log the offending string length to find the truncation bug upstream"],"exampleFix":"// before\nbyte[] b = SaHexUtil.hexToBytes(hex); // hex = \"abc\" -> throws\n\n// after\nif (hex.length() % 2 != 0) hex = \"0\" + hex;\nbyte[] b = SaHexUtil.hexToBytes(hex);","handlingStrategy":"validation","validationCode":"public static boolean isEvenLengthHex(String s) {\n    return s != null && s.length() % 2 == 0\n        && s.matches(\"[0-9a-fA-F]*\");\n}\n// if only length is the concern:\nif (hex.length() % 2 != 0) hex = \"0\" + hex;","typeGuard":"public static boolean isValidHexString(String s) {\n    return s != null && s.length() % 2 == 0 && s.matches(\"(?:[0-9a-fA-F]{2})+\");\n}","tryCatchPattern":"try {\n    byte[] b = SaHexUtil.hexToBytes(hex);\n} catch (IllegalArgumentException e) {\n    // input bug upstream: log hex.length() and reject the value\n}","preventionTips":["Never route hex through numeric types that strip leading zeros (BigInteger.toString)","Validate length parity before decoding and left-pad with '0'","Use fixed-length hex rendering (String.format(\"%02x\", b)) when producing digests"],"tags":["sa-token","hex","encoding","input-validation"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}