bitwarden/server · error · BadRequestException
Emergency access keys cannot be set to null during rotation.
Error message
Emergency access keys cannot be set to null during rotation.
What it means
Thrown by EmergencyAccessRotationValidator when a rotation entry matched a confirmed emergency-access grant (matched by Id) but its KeyEncrypted is null. Rotation must re-encrypt every existing grant; sending a matched entry with a null key would erase access, which the validator forbids.
Source
Thrown at src/Api/KeyManagement/Validators/EmergencyAccessRotationValidator.cs:46
var existing = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(user.Id);
if (existing == null || existing.Count == 0)
{
return result;
}
// Exclude any emergency access that has not been confirmed yet.
existing = existing.Where(ea => ea.KeyEncrypted != null).ToList();
foreach (var ea in existing)
{
var emergencyAccess = emergencyAccessKeys.FirstOrDefault(c => c.Id == ea.Id);
if (emergencyAccess == null)
{
throw new BadRequestException("All existing emergency access keys must be included in the rotation.");
}
if (emergencyAccess.KeyEncrypted == null)
{
throw new BadRequestException("Emergency access keys cannot be set to null during rotation.");
}
result.Add(emergencyAccess.ToEmergencyAccess(ea));
}
return result;
}
}
View on GitHub (pinned to e93b962371)
Solutions
- Re-encrypt the grant's KeyEncrypted with the new user key for every included grant.
- To remove an emergency-access grant, call the revoke endpoint before rotating rather than nulling the key in the rotation payload.
- Assert every included grant entry has a non-null KeyEncrypted before submitting.
Example fix
// before: trying to drop a grant by nulling
{ id: g.id, keyEncrypted: null }
// after: revoke separately, then rotate the rest
await api.revokeEmergencyAccess(g.id);
rotate({ emergencyAccessKeys: remaining.map(g => ({ id: g.id, keyEncrypted: reencrypt(g.key) })) }); Defensive patterns
Strategy: validation
Validate before calling
const nulled = payload.emergencyAccessKeys.filter(g => g.keyEncrypted == null);
if (nulled.length) {
throw new Error(`Emergency access keys cannot be null: ${nulled.map(g => g.id).join(', ')}`);
} Type guard
function hasEmergencyAccessKey(g: { keyEncrypted?: string | null }): boolean {
return g.keyEncrypted != null && g.keyEncrypted.length > 0;
} Try / catch
try {
await api.rotateKey(payload);
} catch (e) {
if (e.status === 400 && /cannot be set to null/i.test(e.message)) {
payload.emergencyAccessKeys = payload.emergencyAccessKeys.map(g => ({ ...g, keyEncrypted: reencrypt(g.keyEncrypted ?? oldKeyFor(g.id)) }));
return api.rotateKey(payload);
}
throw e;
} Prevention
- Never null a key to 'remove' a grant; use the revoke endpoint instead.
- Assert every included grant has a non-null KeyEncrypted before submitting.
- Verify re-encryption succeeded for each grant before adding it to the payload.
When it happens
Trigger: The emergency-access array includes the grant Id but the KeyEncrypted field was left null or stripped out during request construction.
Common situations: Client attempted to 'remove' a grant by nulling its key instead of revoking it through the revoke endpoint; serialization omitted the field; the re-encryption of that grant's key failed silently.
Related errors
- Rotated encryption keys must be provided for all devices tha
- All existing emergency access keys must be included in the r
- Reset Password keys cannot be set to null during rotation.
- WebAuthn prf keys must have user-key during rotation.
- WebAuthn prf keys must have public-key during rotation.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/2e5f1dd6612ba48b.
Report an issue: GitHub.