bitwarden/server · error · BadRequestException
Rotated encryption keys must be provided for all devices tha
Error message
Rotated encryption keys must be provided for all devices that are trusted.
What it means
Thrown by DeviceRotationValidator when a device in the rotation request matched an existing trusted device, but its EncryptedUserKey or EncryptedPublicKey is null. Trusted devices must be re-encrypted with the new user key, so submitting a matched entry without the freshly-encrypted keys is treated as an incomplete rotation and rejected.
Source
Thrown at src/Api/KeyManagement/Validators/DeviceRotationValidator.cs:45
var result = new List<Device>();
var existingTrustedDevices = (await _deviceRepository.GetManyByUserIdAsync(user.Id)).Where(d => d.IsTrusted()).ToList();
if (existingTrustedDevices.Count == 0)
{
return result;
}
foreach (var existing in existingTrustedDevices)
{
var device = devices.FirstOrDefault(c => c.DeviceId == existing.Id);
if (device == null)
{
throw new BadRequestException("All existing trusted devices must be included in the rotation.");
}
if (device.EncryptedUserKey == null || device.EncryptedPublicKey == null)
{
throw new BadRequestException("Rotated encryption keys must be provided for all devices that are trusted.");
}
result.Add(device.ToDevice(existing));
}
return result;
}
}
View on GitHub (pinned to e93b962371)
Solutions
- Re-encrypt both the user key and public key for every trusted device using the new user key before submitting.
- Add a pre-send assertion that every device entry has non-null EncryptedUserKey and EncryptedPublicKey.
- If a trusted device's old keys cannot be read, un-trust the device first so it is excluded from rotation.
- Inspect the failing entry's DeviceId in the client logs to find which device was under-populated.
Example fix
// before: entry missing keys
{ deviceId: d.id }
// after: both keys re-encrypted with new user key
{ deviceId: d.id, encryptedUserKey: reencrypt(d.userKey), encryptedPublicKey: reencrypt(d.publicKey) } Defensive patterns
Strategy: validation
Validate before calling
// Ensure every submitted trusted device has both keys
const incomplete = payload.devices.filter(d =>
trustedIds.has(d.deviceId) && (!d.encryptedUserKey || !d.encryptedPublicKey));
if (incomplete.length) {
throw new Error(`Missing re-encrypted keys for: ${incomplete.map(d => d.deviceId).join(', ')}`);
} Type guard
function hasDeviceKeys(d: { encryptedUserKey?: string; encryptedPublicKey?: string }): boolean {
return !!d.encryptedUserKey && !!d.encryptedPublicKey;
} Try / catch
try {
await api.rotateKey(payload);
} catch (e) {
if (e.status === 400 && /keys must be provided for all devices/i.test(e.message)) {
payload.devices = payload.devices.map(d => reencryptDevice(d));
return api.rotateKey(payload);
}
throw e;
} Prevention
- Run a pre-submit assertion that each trusted-device entry has non-null EncryptedUserKey and EncryptedPublicKey.
- Centralize re-encryption in a single mapper so no field is forgotten.
- Unit-test the request builder against a device fixture to catch missing keys.
When it happens
Trigger: The rotation payload includes the DeviceId of a trusted device but the entry was constructed without populating EncryptedUserKey and/or EncryptedPublicKey (e.g. a shallow copy, a placeholder object, or the re-encryption step was skipped for that device).
Common situations: Client reused a request template and forgot to set the key fields on a newly-trusted device; the device's old keys failed to decrypt so the client skipped re-encryption; a serialization bug dropped null-suppressed fields.
Related errors
- All existing trusted devices must be included in the rotatio
- Emergency access keys cannot be set to null during rotation.
- 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/cb7b3e6c849d9882.
Report an issue: GitHub.