spring-projects/spring-security · error · IllegalArgumentException
Bad strength
Error message
Bad strength
What it means
The BCryptPasswordEncoder constructor validates the requested strength (log rounds) when explicitly provided. It must be either -1 (meaning 'use the default of 10') or within BCrypt.MIN_LOG_ROUNDS (4) to BCrypt.MAX_LOG_ROUNDS (31). Anything else throws IllegalArgumentException at construction time.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java:106
/**
* Creates a new instance using the given bcrypt version and strength.
* @param version the version of bcrypt, can be 2a,2b,2y
* @param strength the log rounds to use, between 4 and 31
*/
public BCryptPasswordEncoder(BCryptVersion version, int strength) {
this(version, strength, null);
}
/**
* Creates a new instance using the given bcrypt version, strength, and secure random
* instance.
* @param version the version of bcrypt, can be 2a,2b,2y
* @param strength the log rounds to use, between 4 and 31
* @param random the secure random instance to use
*/
public BCryptPasswordEncoder(BCryptVersion version, int strength, @Nullable SecureRandom random) {
if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
throw new IllegalArgumentException("Bad strength");
}
this.version = version;
this.strength = (strength == -1) ? 10 : strength;
this.random = (random != null) ? () -> random : SecureRandomHolder::getInstance;
}
@Override
protected String encodeNonNullPassword(String rawPassword) {
String salt = getSalt();
return BCrypt.hashpw(rawPassword.toString(), salt);
}
private String getSalt() {
return BCrypt.gensalt(this.version.getVersion(), this.strength, this.random.get());
}
@Override
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {View on GitHub (pinned to 96852e8860)
Solutions
- Use a strength between 4 and 31 (or -1 for the default).
- Validate the configured integer before constructing the encoder.
- Omit the strength parameter and use the no-arg or (version) constructor if defaults are fine.
Example fix
// before
int strength = Integer.parseInt(env.getProperty("security.bcrypt.strength"));
PasswordEncoder encoder = new BCryptPasswordEncoder(BCryptVersion.$2A, strength);
// after
int raw = Integer.parseInt(env.getProperty("security.bcrypt.strength"));
if (raw != -1 && (raw < 4 || raw > 31)) throw new IllegalStateException("bcrypt strength must be 4-31 or -1");
PasswordEncoder encoder = new BCryptPasswordEncoder(BCryptVersion.$2A, raw); Defensive patterns
Strategy: validation
Validate before calling
if (strength != -1 && (strength < 4 || strength > 31)) throw new IllegalStateException("strength must be 4-31 or -1: " + strength); Try / catch
try { encoder = new BCryptPasswordEncoder(version, strength, random); } catch (IllegalArgumentException e) { throw new IllegalStateException("Invalid bcrypt strength configuration: " + strength, e); } Prevention
- Validate @Value-injected strength properties at application startup (fail-fast bean validation).
- Use -1 sentinel or omit the parameter for defaults.
- Never compute strength from percentages or bit sizes; store plain log-round integers.
When it happens
Trigger: new BCryptPasswordEncoder(BCryptVersion.$2A, strength, random) where strength is, e.g., 0, 3, 32, or 100 — typically from externalized configuration or a computed value — while not being exactly -1.
Common situations: Spring @Value("${bcrypt.strength}") injecting an unvalidated property; storing strength as a percentage (e.g. 80); front-end sending cost as bits instead of rounds; typos like -11.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/27b9ce2cda98a10a.
Report an issue: GitHub.