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

  1. Fix the data source so the String contains valid Unicode (no unpaired surrogates).
  2. Replace malformed chars before encoding: cs.toString().replaceAll("[\\uD800-\\uDFFF]", "") when lossy cleanup is acceptable.
  3. 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

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


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