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

  1. Inspect the reported positions and correct the characters to valid hex digits.
  2. Sanitize input first: strip whitespace and validate with hexStr.matches("[0-9a-fA-F]+") before decoding.
  3. 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

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/3b6478d3e47d4ccd. Report an issue: GitHub.