spring-projects/spring-security · error · IllegalArgumentException
Invalid prefix
Error message
Invalid prefix
What it means
BCrypt.gensalt throws this when the salt prefix string is not a valid BCrypt identifier. A valid prefix must start with "$2" and its third character must be 'a', 'y', or 'b' (the supported BCrypt versions 2a, 2y, 2b). The library throws IllegalArgumentException to fail fast rather than emitting a salt that no BCrypt implementation can verify.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java:692
return rs.toString();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param prefix the prefix value (default $2a)
* @param log_rounds the log2 of the number of rounds of hashing to apply - the work
* factor therefore increases as 2**log_rounds.
* @param random an instance of SecureRandom to use
* @return an encoded salt value
* @exception IllegalArgumentException if prefix or log_rounds is invalid
*/
public static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException {
StringBuilder rs = new StringBuilder();
byte rnd[] = new byte[BCRYPT_SALT_LEN];
if (!prefix.startsWith("$2")
|| (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {
throw new IllegalArgumentException("Invalid prefix");
}
if (log_rounds < 4 || log_rounds > 31) {
throw new IllegalArgumentException("Invalid log_rounds");
}
random.nextBytes(rnd);
rs.append("$2");
rs.append(prefix.charAt(2));
rs.append("$");
if (log_rounds < 10) {
rs.append("0");
}
rs.append(log_rounds);
rs.append("$");
encode_base64(rnd, rnd.length, rs);
return rs.toString();
}View on GitHub (pinned to 96852e8860)
Solutions
- Pass one of the exact prefixes "$2a", "$2y", or "$2b" (or use BCryptPasswordEncoder's BCryptVersion enum instead of a raw string).
- If you have an existing encoded hash, extract the prefix via hash.substring(0, 3) rather than passing the whole hash.
- Validate prefix.startsWith("$2") && "ayb".indexOf(prefix.charAt(2)) >= 0 before calling to fail with a clearer message.
Example fix
// before
BCrypt.gensalt("$2x", 10);
// after
BCrypt.gensalt("$2a", 10); // or BCrypt.gensalt(BCrypt.BCryptVersion.$2A) Defensive patterns
Strategy: validation
Validate before calling
boolean validPrefix(String p) { return p != null && p.length() >= 3 && p.startsWith("$2") && (p.charAt(2)=='a' || p.charAt(2)=='y' || p.charAt(2)=='b'); } Prevention
- Use BCrypt.BCryptVersion enum or BCryptPasswordEncoder instead of raw prefix strings.
- Never pass a full hash or a foreign crypt prefix where a BCrypt prefix is expected.
- Extract prefixes from stored hashes with substring(0, 3) only after format validation.
When it happens
Trigger: Calling BCrypt.gensalt(prefix, log_rounds, random) with a prefix like "$1" (MD5 crypt), "$2" with no version char, "$2c", "$2x", or an empty string — anything failing the startsWith("$2") or charAt(2) in {'a','y','b'} check.
Common situations: Porting salt strings from other crypt implementations (e.g. $1$ or $6$ prefixes from glibc crypt), hand-copying hashed salts truncated to two characters, or passing a full hash as the prefix instead of just the "$2a" portion.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/fb096873b5050d37.
Report an issue: GitHub.