bitwarden/server · error · BadRequestException
The model state is invalid.
Error message
The model state is invalid.
What it means
In POST /accounts/kdf, when _changeKdfCommand.ChangeKdfAsync returns a failed result, each IdentityError is added to ModelState, a 2-second delay is applied, and BadRequestException(ModelState) is thrown → HTTP 400. The change-KDF command validates the master password and the re-encrypted authentication/unlock data; any failure surfaces here.
Source
Thrown at src/Api/Auth/Controllers/AccountsController.cs:371
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var result = await _changeKdfCommand.ChangeKdfAsync(user, model.MasterPasswordHash, model.AuthenticationData.ToData(), model.UnlockData.ToData());
if (result.Succeeded)
{
return;
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
await Task.Delay(2000);
throw new BadRequestException(ModelState);
}
[HttpPost("security-stamp")]
public async Task PostSecurityStamp([FromBody] SecretVerificationRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var result = await _userService.RefreshSecurityStampAsync(user, model.Secret);
if (result.Succeeded)
{
return;
}
foreach (var error in result.Errors)View on GitHub (pinned to e93b962371)
Solutions
- Inspect ValidationErrors for the specific failure (password vs. re-encryption vs. parameter range).
- Re-derive MasterPasswordHash from the correct, current master password and KDF settings.
- Rebuild AuthenticationData/UnlockData by re-encrypting the user key with the NEW KDF-derived key on a fresh, complete vault sync.
- Validate the requested KDF parameters against the server's allowed ranges before submitting.
Example fix
// before model.AuthenticationData = ReencryptWith(oldKdf); // after model.MasterPasswordHash = DeriveHash(password, newKdf); model.AuthenticationData = ReencryptUserKey(userKey, password, newKdf); model.UnlockData = ReencryptWith(newKdf);
Defensive patterns
Strategy: validation
Validate before calling
// Validate KDF target params and re-encrypt with a complete vault before posting
if (!KdfParamsValid(newKdf)) errors.Add("KDF params out of range");
if (!vaultFullySynced) errors.Add("Sync vault before KDF change"); Try / catch
try { await client.PostKdfAsync(model); }
catch (BadRequestException ex) { /* read ex.ModelState: password vs re-encryption vs params */ } Prevention
- Re-encrypt the entire vault with the new KDF before submitting.
- Validate Argon2id memory/parallelism against server minimums.
- Derive MasterPasswordHash from the correct current password.
When it happens
Trigger: Wrong master password hash supplied with the KDF change; AuthenticationData or UnlockData that fail to decrypt/re-encrypt under the new KDF; invalid target KDF parameters (e.g. Argon2id with out-of-range memory/parallelism).
Common situations: User mistyped the master password during KDF migration; client built the re-encrypted payload with stale vault keys; Argon2id memory/iterations below server minimums; vault has ciphers the client failed to include in the re-encryption payload.
Related errors
- AccountKeys are only supported for V2 encryption.
- All existing trusted devices must be included in the rotatio
- All existing emergency access keys must be included in the r
- All existing folders must be included in the rotation.
- All existing reset password keys must be included in the rot
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/c01bdc9f236a61c3.
Report an issue: GitHub.