spring-projects/spring-security · error · IllegalArgumentException
Invalid log_rounds
Error message
Invalid log_rounds
What it means
BCrypt.gensalt throws this when the log_rounds (cost factor) is outside the supported range of 4 to 31. log_rounds determines the work factor as 2^rounds key-expansion iterations; values below 4 are insecure and above 31 overflow the algorithm. The library rejects such values with IllegalArgumentException.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java:695
/**
* 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();
}
/**
* Generate a salt for use with the BCrypt.hashpw() methodView on GitHub (pinned to 96852e8860)
Solutions
- Use a log_rounds value between 4 and 31 (10 is the typical default).
- Clamp or validate the value: Math.max(4, Math.min(31, strength)) before calling.
- Prefer BCryptPasswordEncoder, which also accepts -1 meaning 'use default 10'.
Example fix
// before
int rounds = Integer.parseInt(props.getProperty("bcrypt.rounds")); // e.g. 40
String salt = BCrypt.gensalt("$2a", rounds);
// after
int rounds = Math.min(31, Math.max(4, Integer.parseInt(props.getProperty("bcrypt.rounds"))));
String salt = BCrypt.gensalt("$2a", rounds); Defensive patterns
Strategy: validation
Validate before calling
boolean validRounds(int r) { return r >= 4 && r <= 31; } Prevention
- Centralize strength selection in one validated constant/config accessor.
- Clamp externalized config values to [4, 31] at load time.
- Prefer the documented default (10) unless profiling justifies another value.
When it happens
Trigger: Calling BCrypt.gensalt(prefix, log_rounds, random) with log_rounds < 4 or > 31, e.g. gensalt("$2a", 2) or gensalt("$2a", 32), or passing a strength parsed from config/user input without bounds checking.
Common situations: Making strength configurable via properties and reading an unvalidated int; typo'd defaults like 0 or -1; copying Node.js bcryptjs cost values of 3; or attempting extremely high cost factors on modern hardware.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/2165b96a5c4b74fe.
Report an issue: GitHub.