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() method

View on GitHub (pinned to 96852e8860)

Solutions

  1. Use a log_rounds value between 4 and 31 (10 is the typical default).
  2. Clamp or validate the value: Math.max(4, Math.min(31, strength)) before calling.
  3. 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

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.