spring-projects/spring-security · error · IllegalArgumentException
Invalid encoded Argon2-hash
Error message
Invalid encoded Argon2-hash
What it means
Argon2EncodingUtils.decode() splits the PHC-style encoded hash on '$' and requires at least 4 segments (algorithm, params, salt, hash). If the string has fewer parts, it cannot possibly represent a valid Argon2 hash, so an IllegalArgumentException is thrown. This guards against corrupt or truncated hash strings before parameter parsing begins.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java:107
* {@code $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>}
*
* where {@code <T>} is either 'd', 'id', or 'i', {@code <num>} is a decimal integer
* (positive, fits in an 'unsigned long'), and {@code <bin>} is Base64-encoded data
* (no '=' padding characters, no newline or whitespace).
*
* The last two binary chunks (encoded in Base64) are, in that order, the salt and the
* output. Both are required. The binary salt length and the output length must be in
* the allowed ranges defined in argon2.h.
* @param encodedHash the Argon2 hash string as described above
* @return an {@link Argon2Hash} object containing the raw hash and the
* {@link Argon2Parameters}.
* @throws IllegalArgumentException if the encoded hash is malformed
*/
static Argon2Hash decode(String encodedHash) throws IllegalArgumentException {
Argon2Parameters.Builder paramsBuilder;
String[] parts = encodedHash.split("\\$");
if (parts.length < 4) {
throw new IllegalArgumentException("Invalid encoded Argon2-hash");
}
int currentPart = 1;
paramsBuilder = switch (parts[currentPart++]) {
case "argon2d" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d);
case "argon2i" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i);
case "argon2id" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id);
default -> throw new IllegalArgumentException("Invalid algorithm type: " + parts[1]);
};
if (parts[currentPart].startsWith("v=")) {
paramsBuilder.withVersion(Integer.parseInt(parts[currentPart].substring(2)));
currentPart++;
}
String[] performanceParams = parts[currentPart++].split(",");
if (performanceParams.length != 3) {
throw new IllegalArgumentException("Amount of performance parameters invalid");
}
if (!performanceParams[0].startsWith("m=")) {
throw new IllegalArgumentException("Invalid memory parameter");View on GitHub (pinned to 96852e8860)
Solutions
- Verify the stored hash is a complete Argon2 PHC string starting with '$argon2id$' (or argon2i/argon2d) and re-generate it with Argon2PasswordEncoder.encode() if not
- Check the persistence layer (column length, trim-on-save) is not truncating the hash
- Validate the hash format in your own code before calling decode()
Example fix
// before
Argon2Hash hash = Argon2EncodingUtils.decode(storedValue);
// after
if (storedValue == null || !storedValue.matches("\\$argon2(id|i|d)\\$.+")) {
throw new IllegalStateException("Stored value is not an Argon2 hash");
}
Argon2Hash hash = Argon2EncodingUtils.decode(storedValue); Defensive patterns
Strategy: validation
Validate before calling
static boolean isArgon2Hash(String s) {
return s != null && s.matches("\\$argon2(id|i|d)\\$v=\\d+\\$m=\\d+,t=\\d+,p=\\d+\\$[A-Za-z0-9+/]+\$[A-Za-z0-9+/]+");
} Type guard
if (encodedHash == null || encodedHash.chars().filter(c -> c == '$').count() < 4) { throw new IllegalArgumentException("not a PHC Argon2 hash"); } Try / catch
try {
Argon2Hash h = Argon2EncodingUtils.decode(encodedHash);
} catch (IllegalArgumentException e) {
throw new InvalidStoredHashException("Stored credential is not a valid Argon2 hash", e);
} Prevention
- Store hashes only via Argon2PasswordEncoder.encode()
- Check DB column length (a full PHC Argon2 hash is ~97+ chars)
- Regex-validate hashes at the ingestion boundary
- Never paste hashes from other algorithms into Argon2 storage
When it happens
Trigger: Calling Argon2EncodingUtils.decode(encodedHash) with a string that splits into fewer than 4 '$'-separated parts — e.g. null-derived empty string, a truncated hash, a plain-text password, or a hash from another algorithm (like a bare bcrypt fragment).
Common situations: Database column truncated the stored hash; user-supplied hash pasted incorrectly; migrating from another hasher and feeding its output to Argon2's decoder; loading config where the hash env var is empty.
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
- Amount of performance parameters invalid
- Invalid memory parameter
- Invalid iterations parameter
- Invalid parallelity parameter
- Invalid algorithm type: X
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/e5fd4bf24d8f304d.
Report an issue: GitHub.