spring-projects/spring-security · error · IllegalArgumentException
Encoding failed
Error message
Encoding failed
What it means
Utf8.encode wraps the charset encoder, which throws CharacterCodingException when the CharSequence contains sequences of unpaired surrogates or otherwise malformed character data that cannot be encoded in UTF-8. The library rethrows it as an IllegalArgumentException with cause "Encoding failed".
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/codec/Utf8.java:56
private Utf8() {
}
/**
* Get the bytes of the String in UTF-8 encoded form.
*/
public static byte[] encode(CharSequence string) {
if (string == null) {
throw new IllegalArgumentException("String cannot be null");
}
try {
ByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));
byte[] bytesCopy = new byte[bytes.limit()];
System.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit());
return bytesCopy;
}
catch (CharacterCodingException ex) {
throw new IllegalArgumentException("Encoding failed", ex);
}
}
/**
* Decode the bytes in UTF-8 form into a String.
*/
public static String decode(byte[] bytes) {
try {
return CHARSET.newDecoder().decode(ByteBuffer.wrap(bytes)).toString();
}
catch (CharacterCodingException ex) {
throw new IllegalArgumentException("Decoding failed", ex);
}
}
/**
* Constant time comparison to prevent against timing attacks.
* @param expected the expected {@link CharSequence}View on GitHub (pinned to 96852e8860)
Solutions
- Fix the data source so the String contains valid Unicode (no unpaired surrogates).
- Replace malformed chars before encoding: cs.toString().replaceAll("[\\uD800-\\uDFFF]", "") when lossy cleanup is acceptable.
- If the input is really binary, use byte[] end-to-end instead of routing through String.
Example fix
// before byte[] out = Utf8.encode(corruptedString); // unpaired surrogate // after String clean = corruptedString.codePoints().filter(cp -> Character.isValidCodePoint(cp) && !Character.isSurrogate((char) cp) || cp >= 0x10000 || cp < 0xD800 || cp > 0xDFFF).collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString(); byte[] out = Utf8.encode(clean);
Defensive patterns
Strategy: try-catch
Validate before calling
boolean encodable(CharSequence cs) { for (int i = 0; i < cs.length(); i++) { char c = cs.charAt(i); if (Character.isHighSurrogate(c) && (i + 1 >= cs.length() || !Character.isLowSurrogate(cs.charAt(i + 1)))) return false; } return true; } Try / catch
try { bytes = Utf8.encode(s); } catch (IllegalArgumentException e) { /* malformed unicode: sanitize or reject */ } Prevention
- Never build Strings from binary data using arbitrary charsets.
- Avoid slicing strings at arbitrary indices that may split surrogate pairs.
- Keep binary payloads as byte[] rather than round-tripping through String.
When it happens
Trigger: Calling Utf8.encode on a String containing isolated/unpaired surrogates (e.g. built from invalid char data read from a binary stream or a corrupted substring operation splitting a surrogate pair).
Common situations: Binary data read into Strings via wrong charset then re-encoded; string slicing that cut a surrogate pair in half; data deserialized from a broken serialization path.
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
- Decoding failed
- 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/abcd00b076acd499.
Report an issue: GitHub.