ruvnet/ruflo · error · PasswordHashError
INVALID_ROUNDS
INVALID_ROUNDS
Error message
Bcrypt rounds must be between 10 and 20 for security and performance balance
What it means
PasswordHasher's constructor validates the bcrypt cost factor and refuses rounds outside [10, 20] with PasswordHashError INVALID_ROUNDS. Below 10, hashes become brute-forceable; above 20, hashing/verification gets slow enough to enable denial-of-service on auth paths. The default is 12.
Source
Thrown at v3/@claude-flow/security/src/password-hasher.ts:114
* ```
*/
export class PasswordHasher {
private readonly config: Required<PasswordHasherConfig>;
constructor(config: PasswordHasherConfig = {}) {
this.config = {
rounds: config.rounds ?? 12,
minLength: config.minLength ?? 8,
maxLength: config.maxLength ?? 128,
requireUppercase: config.requireUppercase ?? true,
requireLowercase: config.requireLowercase ?? true,
requireDigit: config.requireDigit ?? true,
requireSpecial: config.requireSpecial ?? false,
};
// Validate configuration
if (this.config.rounds < 10 || this.config.rounds > 20) {
throw new PasswordHashError(
'Bcrypt rounds must be between 10 and 20 for security and performance balance',
'INVALID_ROUNDS'
);
}
if (this.config.minLength < 8) {
throw new PasswordHashError(
'Minimum password length must be at least 8 characters',
'INVALID_MIN_LENGTH'
);
}
}
/**
* Validates password against configured requirements.
*
* @param password - The password to validate
* @returns Validation result with errors if anyView on GitHub (pinned to fa13ee4ad6)
Solutions
- Set rounds between 10 and 20 (12 — the default — is the usual sweet spot)
- Parse and range-check env-provided values before constructing the hasher
- If a policy truly demands >20, benchmark the verify path first and account for the latency cost on every login
Example fix
// before
new PasswordHasher({ rounds: 4 });
// after
new PasswordHasher({ rounds: 12 }); Defensive patterns
Strategy: validation
Validate before calling
const rounds = Number(process.env.BCRYPT_ROUNDS ?? 12);
if (!(rounds >= 10 && rounds <= 20)) {
throw new Error(`bcrypt rounds must be 10-20, got ${rounds}`);
}
new PasswordHasher({ rounds }); Type guard
function isPasswordHashError(e: unknown, code?: string): boolean {
return e instanceof Error && e.name === 'PasswordHashError'
&& (code === undefined || (e as { code?: string }).code === code);
} Try / catch
try {
return new PasswordHasher(cfg);
} catch (e) {
if (isPasswordHashError(e, 'INVALID_ROUNDS')) {
return new PasswordHasher({ ...cfg, rounds: 12 }); // fall back to the default
}
throw e;
} Prevention
- Range-check env-provided bcrypt rounds at config load, not at hash time
- Omit rounds to accept the default 12 unless you've benchmarked otherwise
- Remember rounds affect every hash AND verify — budget login latency when changing it
When it happens
Trigger: new PasswordHasher({ rounds: 4 }) copied from a benchmark or legacy config; rounds supplied via env as a string that slips through unvalidated; raising rounds above 20 for 'extra security' without benchmarking login latency.
Common situations: Configs ported from other bcrypt wrappers with lower floors; performance tuning dropping below 10; compliance-driven increases past 20 that stall auth throughput.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- INVALID_PASSWORD_LENGTH
- INVALID_API_KEY_LENGTH
- INVALID_SECRET_LENGTH
- INVALID_MIN_LENGTH
- VALIDATION_FAILED
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/9833cd7c6f75c8a2.
Report an issue: GitHub.