spring-projects/spring-security · error · IllegalArgumentException
Invalid salt
Error message
Invalid salt
What it means
BCrypt.hashpw() validates that the salt string is at least 28 characters before parsing. A shorter string cannot contain the bcrypt modular format ($2a$NN$ + 22-char salt), so "Invalid salt" is thrown.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java:625
BCrypt B;
String real_salt;
byte saltb[], hashed[];
char minor = (char) 0;
int rounds, off;
StringBuilder rs = new StringBuilder();
// Enforce max length for new passwords only
if (!for_check && passwordb.length > 72) {
throw new IllegalArgumentException("password cannot be more than 72 bytes");
}
if (salt == null) {
throw new IllegalArgumentException("salt cannot be null");
}
int saltLength = salt.length();
if (saltLength < 28) {
throw new IllegalArgumentException("Invalid salt");
}
if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
throw new IllegalArgumentException("Invalid salt version");
}
if (salt.charAt(2) == '$') {
off = 3;
}
else {
minor = salt.charAt(2);
if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b') || salt.charAt(3) != '$') {
throw new IllegalArgumentException("Invalid salt revision");
}
off = 4;
}
// Extract number of rounds
if (salt.charAt(off + 2) > '$') {View on GitHub (pinned to 96852e8860)
Solutions
- Pass the FULL stored hash (including $2a$10$ prefix) as the salt when verifying
- Store hashes in a column of at least 60 chars to avoid truncation
- Use BCryptPasswordEncoder.matches(rawPassword, storedHash) rather than manual hashpw
Example fix
// before String hash = BCrypt.hashpw(pw, storedHash.substring(0, 22)); // after String hash = BCrypt.hashpw(pw, storedHash); // full 60-char hash string
Defensive patterns
Strategy: validation
Validate before calling
if (salt == null || salt.length() < 28 || !salt.startsWith("$2")) {
throw new IllegalArgumentException("Not a valid bcrypt salt string");
} Type guard
boolean isBcryptFormat(String s) {
return s != null && s.length() >= 28 && s.startsWith("$2");
} Try / catch
try {
hash = BCrypt.hashpw(pw, salt);
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Invalid salt")) { /* treat as corrupt record / force reset */ }
} Prevention
- Store full 60-char bcrypt hashes; size columns >= 60
- Use BCryptPasswordEncoder.matches() which handles salt extraction
- Never pass a bare base64 salt without its $2a$NN$ prefix
When it happens
Trigger: Passing a truncated hash, a raw 22-character base64 salt without the $2a$ prefix, an empty string, or a plain-text value to hashpw as the salt parameter.
Common situations: Database column sized too small so stored hashes got truncated; slicing only the salt portion out of a stored hash then passing it back for verification; storing the result of gensalt incorrectly.
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/860b8e938fa45b53.
Report an issue: GitHub.