spring-projects/spring-security · error · IllegalArgumentException
Detected a Non-hex character at N or N+1 position
Error message
Detected a Non-hex character at N or N+1 position
What it means
Hex.decode validates each character pair with Character.digit(c, 16). If either character of a pair is not a hex digit (0-9, a-f, A-F), the library throws IllegalArgumentException identifying the 1-based positions (i+1 or i+2) where the bad character may be. Note both positions of the offending pair are reported since it doesn't say which one failed.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/codec/Hex.java:58
// Char for top 4 bits
result[j++] = HEX[(0xF0 & aByte) >>> 4];
// Bottom 4
result[j++] = HEX[(0x0F & aByte)];
}
return result;
}
public static byte[] decode(CharSequence s) {
int nChars = s.length();
if (nChars % 2 != 0) {
throw new IllegalArgumentException("Hex-encoded string must have an even number of characters");
}
byte[] result = new byte[nChars / 2];
for (int i = 0; i < nChars; i += 2) {
int msb = Character.digit(s.charAt(i), 16);
int lsb = Character.digit(s.charAt(i + 1), 16);
if (msb < 0 || lsb < 0) {
throw new IllegalArgumentException(
"Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
}
result[i / 2] = (byte) ((msb << 4) | lsb);
}
return result;
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Inspect the reported positions and correct the characters to valid hex digits.
- Sanitize input first: strip whitespace and validate with hexStr.matches("[0-9a-fA-F]+") before decoding.
- Fix the producer to emit proper hex (e.g. String.format("%02x", b)) instead of Base64 or raw chars.
Example fix
// before
byte[] key = Hex.decode(raw.trim()); // may contain 'g' or spaces
// after
String clean = raw.replaceAll("\\s", "");
if (!clean.matches("[0-9a-fA-F]+")) throw new IllegalArgumentException("not hex: " + clean);
byte[] key = Hex.decode(clean); Defensive patterns
Strategy: validation
Validate before calling
boolean isHex(CharSequence s) { return s != null && s.length() % 2 == 0 && s.chars().allMatch(c -> Character.digit(c, 16) >= 0); } Try / catch
try { bytes = Hex.decode(s); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Bad hex at/near: " + e.getMessage(), e); } Prevention
- Run a strict hex regex ([0-9a-fA-F]+) over input before decode.
- Reject or trim input containing whitespace, 0x prefixes, or punctuation.
- Generate hex with a canonical encoder so round-trips are guaranteed valid.
When it happens
Trigger: Calling Hex.decode("zz12") or any string containing 'g'-'z', punctuation, whitespace (e.g. "ab cd"), or unicode characters — even length but non-hex content.
Common situations: Hashes copied from logs with embedded spaces or line breaks; uppercase prefix like "0X"; Base64 data mistakenly treated as hex; locale-dependent case mangling.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Hex-encoded string must have an even number of characters
- String cannot be null
- Access is denied
- RunAsImplAuthenticationProvider.incorrectKey
- Access is denied
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/3b6478d3e47d4ccd.
Report an issue: GitHub.