spring-projects/spring-security · error · IllegalArgumentException

Decoding failed

Error message

Decoding failed

What it means

Utf8.decode converts UTF-8 bytes to a String using the charset decoder, which throws CharacterCodingException when the byte array is not valid UTF-8 (malformed lead bytes, truncated multi-byte sequences, or invalid continuation bytes). The library rethrows it as IllegalArgumentException with cause "Decoding failed".

Source

Thrown at crypto/src/main/java/org/springframework/security/crypto/codec/Utf8.java:68

			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}
	 * @param actual the actual {@link CharSequence}
	 * @return true if {@code expected} and {@code actual} are equal, false otherwise
	 * @since 5.7.26
	 */
	public static boolean isEqual(@Nullable CharSequence expected, @Nullable CharSequence actual) {
		byte[] expectedBytes = bytesUtf8(expected);
		byte[] actualBytes = bytesUtf8(actual);
		return MessageDigest.isEqual(expectedBytes, actualBytes);
	}

	private static byte @Nullable [] bytesUtf8(@Nullable CharSequence s) {
		return (s != null) ? Utf8.encode(s) : null;

View on GitHub (pinned to 96852e8860)

Solutions

  1. Decode with the byte data's actual charset, or convert/repair the data to UTF-8.
  2. If bytes may be arbitrary binary, do not use Utf8.decode — keep them as byte[] or use Base64.
  3. Check truncation logic so byte arrays are not cut in the middle of a multi-byte sequence.

Example fix

// before
String s = Utf8.decode(latin1Bytes); // throws
// after
String s = new String(latin1Bytes, StandardCharsets.ISO_8859_1); // correct source charset
Defensive patterns

Strategy: validation

Validate before calling

boolean isProbablyUtf8(byte[] b) { int i = 0; while (i < b.length) { if (b[i] >= 0) { i++; } else if ((b[i] & 0xE0) == 0xC0 && i + 1 < b.length && (b[i+1] & 0xC0) == 0x80) { i += 2; } else if ((b[i] & 0xF0) == 0xE0 && i + 2 < b.length && (b[i+1] & 0xC0) == 0x80 && (b[i+2] & 0xC0) == 0x80) { i += 3; } else return false; } return true; }

Try / catch

try { s = Utf8.decode(bytes); } catch (IllegalArgumentException e) { s = new String(bytes, fallbackCharset); }

Prevention

When it happens

Trigger: Calling Utf8.decode on bytes that are not UTF-8 — e.g. Latin-1/GBK/Windows-1252 encoded text, encrypted or compressed bytes, or a byte array truncated mid multi-byte character.

Common situations: Reading files produced on a legacy system with a different default charset; decoding database blobs stored in another encoding; slicing byte arrays at fixed offsets cutting a multi-byte char.

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/09ea9a84e2d1a20a. Report an issue: GitHub.