bitwarden/server · error · BadRequestException
All existing trusted devices must be included in the rotatio
Error message
All existing trusted devices must be included in the rotation.
What it means
Thrown by DeviceRotationValidator during a user encryption-key rotation (e.g. master password change). The validator loads every device the user has marked as trusted (devices holding an encrypted copy of the user key for SSO/trust flows) and requires the rotation request to include every one of them, matched by DeviceId. If even one trusted device is absent from the submitted list, the whole rotation is rejected so that no device is left encrypted under the old (now-rotated) key.
Source
Thrown at src/Api/KeyManagement/Validators/DeviceRotationValidator.cs:40
_deviceRepository = deviceRepository;
}
public async Task<IEnumerable<Device>> ValidateAsync(User user, IEnumerable<OtherDeviceKeysUpdateRequestModel> devices)
{
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-fetch the user's full device list immediately before building the rotation payload so no trusted device is omitted.
- Ensure every entry with IsTrusted()=true on the server is present in the request, keyed by its exact DeviceId GUID.
- If a device should no longer be trusted, un-trust/delete it through its own endpoint before rotating keys, rather than dropping it from the rotation list.
- Validate the submitted DeviceId set is a superset of the trusted-device set before sending the request.
Example fix
// before: client sends only devices it knew about
var devices = ["device-a", "device-b"];
rotate({ devices: devices.map(reencrypt) });
// after: fetch the authoritative trusted set first, include all
const trusted = await api.getMyDevices();
const devices = trusted.filter(d => d.isTrusted).map(d => d.id);
rotate({ devices: devices.map(reencrypt) }); Defensive patterns
Strategy: validation
Validate before calling
// Before rotating, ensure every trusted device is represented
const trusted = (await api.getMyDevices()).filter(d => d.isTrusted);
const submitted = new Set(rotationPayload.devices.map(d => d.deviceId));
const missing = trusted.filter(d => !submitted.has(d.id));
if (missing.length) {
throw new Error(`Rotation is missing trusted devices: ${missing.map(d => d.id).join(', ')}`);
} Type guard
function isCompleteDeviceRotation(existing: Device[], submitted: { deviceId: string }[]): boolean {
const have = new Set(submitted.map(s => s.deviceId));
return existing.filter(d => d.isTrusted).every(d => have.has(d.id));
} Try / catch
try {
await api.rotateKey(payload);
} catch (e) {
if (e.status === 400 && /trusted devices must be included/i.test(e.message)) {
await refreshDevices();
payload.devices = trustedDevices.map(reencrypt);
return api.rotateKey(payload);
}
throw e;
} Prevention
- Always re-fetch the user's devices immediately before building a rotation payload.
- Sync the vault/account state right before a key rotation to avoid stale lists.
- Add a client-side superset assertion comparing submitted ids against the trusted-device set.
When it happens
Trigger: PUT/POST to the user key-rotation endpoint with an `OtherDeviceKeysUpdateRequestModel` collection that omits one or more DeviceIds returned by the devices repository for the user. A new device was trusted since the client last enumerated the device list; the client sent a stale/partial list; or a DeviceId was malformed/transposed.
Common situations: A client app cached the device list at session start and a device was trusted in another session; the rotation request was hand-built and only included 'known' devices; concurrent rotations or a race where a device trust completed mid-rotation.
Related errors
- Rotated encryption keys must be provided for all devices tha
- 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
- All existing sends must be included in the rotation.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/a65058e0703acd9a.
Report an issue: GitHub.