spring-projects/spring-security · error · IllegalArgumentException
Hex-encoded string must have an even number of characters
Error message
Hex-encoded string must have an even number of characters
What it means
Hex.decode converts a hexadecimal CharSequence into bytes and requires each byte to be represented by exactly two hex characters. An odd-length string cannot be split into byte pairs, so the library throws IllegalArgumentException immediately instead of guessing a trailing nibble.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/codec/Hex.java:51
}
public static char[] encode(byte[] bytes) {
final int nBytes = bytes.length;
char[] result = new char[2 * nBytes];
int j = 0;
for (byte aByte : bytes) {
// 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
- Fix the source so the hex string has an even number of characters.
- Check s.length() % 2 == 0 before calling and handle the odd case explicitly.
- If the odd string is a lone high nibble, normalize it by prepending '0' (only when the data semantics allow).
Example fix
// before byte[] bytes = Hex.decode(hexStr); // "abc" -> throws // after if (hexStr.length() % 2 != 0) hexStr = "0" + hexStr; byte[] bytes = Hex.decode(hexStr);
Defensive patterns
Strategy: validation
Validate before calling
if (s == null || s.length() % 2 != 0 || !s.chars().allMatch(c -> Character.digit(c, 16) >= 0)) throw new IllegalArgumentException("invalid hex input"); Try / catch
try { bytes = Hex.decode(s); } catch (IllegalArgumentException e) { /* log and reject input */ } Prevention
- Validate hex strings at the input boundary with a [0-9a-fA-F]+ regex and even-length check.
- Strip whitespace/newlines introduced by logs or editors before decoding.
- Keep hex encoding and decoding in one shared utility to avoid asymmetries.
When it happens
Trigger: Calling Hex.decode with a string of odd length, e.g. Hex.decode("abc") or Hex.decode("f"), often from truncated Base16 data or manually built hex strings.
Common situations: Salt or key material pasted from logs missing a character; string concatenation dropping a char; decoding output of a broken encoder; hand-trimmed hashes.
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
- Detected a Non-hex character at N or N+1 position
- 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/7fb434eacf94987c.
Report an issue: GitHub.