dromara/Sa-Token · error · IllegalArgumentException
Invalid hex character at position ${i} or ${i+1}
Error message
Invalid hex character at position ${i} or ${i+1} What it means
The second validation inside SaHexUtil.hexToBytes: after the even-length check passes, each character pair is converted with Character.digit(c, 16); a return of -1 means the char is not a hex digit, and the method throws IllegalArgumentException naming the offending position(s). Any non-[0-9a-fA-F] character — whitespace, '0x' prefix, 'g'-'z', punctuation — triggers it.
Source
Thrown at sa-token-core/src/main/java/cn/dev33/satoken/util/SaHexUtil.java:64
* 将十六进制字符串转换为字节数组(JDK8兼容)
* @param hexString 有效的十六进制字符串(不区分大小写)
* @return 对应的字节数组
* @throws IllegalArgumentException 输入字符串格式错误时抛出异常
*/
public static byte[] hexToBytes(String hexString) {
if (hexString == null) return null;
int len = hexString.length();
if (len % 2 != 0) {
throw new IllegalArgumentException("Hex string must have even length");
}
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int high = Character.digit(hexString.charAt(i), 16);
int low = Character.digit(hexString.charAt(i+1), 16);
if (high == -1 || low == -1) {
throw new IllegalArgumentException(
"Invalid hex character at position " + i + " or " + (i+1)
);
}
data[i/2] = (byte) ((high << 4) + low);
}
return data;
}
}View on GitHub (pinned to ac2c7f6e94)
Solutions
- Strip non-hex characters before decoding: hex = hex.replaceAll("[^0-9a-fA-F]", "")
- Remove '0x' prefixes and trim whitespace/newlines at the source
- Validate first with a regex check: hex.matches("([0-9a-fA-F]{2})*")
Example fix
// before
SaHexUtil.hexToBytes(raw); // raw = "0x1a2b" -> throws
// after
String hex = raw.startsWith("0x") ? raw.substring(2) : raw;
SaHexUtil.hexToBytes(hex.trim()); Defensive patterns
Strategy: type-guard
Validate before calling
String cleaned = hex.trim();
if (cleaned.startsWith("0x") || cleaned.startsWith("0X")) {
cleaned = cleaned.substring(2);
}
cleaned = cleaned.replaceAll("[^0-9a-fA-F]", ""); Type guard
public static boolean isValidHexString(String s) {
return s != null && s.length() % 2 == 0 && s.matches("(?:[0-9a-fA-F]{2})+");
} Try / catch
try {
byte[] b = SaHexUtil.hexToBytes(hex);
} catch (IllegalArgumentException e) {
// e.getMessage() names the offending position; fix the producer of the string
} Prevention
- Strip '0x' prefixes, whitespace and newlines before decoding
- Validate with a hex regex before calling hexToBytes
- When copying digests from terminals, beware hidden line-wrap characters
When it happens
Trigger: hexToBytes("0x1A2B") (contains 'x'), hexToBytes("1a 2b") (space), hexToBytes("hello") — any string with even length but non-hex characters.
Common situations: Digests copied with a '0x' prefix or embedded whitespace/newline (terminal line-wrap); hex read from a file that includes a trailing '\n'; case/charset confusion after passing through a non-hex-safe transformation.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/eefc747d9c666f8f.
Report an issue: GitHub.