{"record":{"id":"19e46102d1a53649","repo":"bitwarden/server","slug":"all-existing-sends-must-be-included-in-the-rotatio","errorCode":null,"errorMessage":"All existing sends must be included in the rotation.","messagePattern":"All existing sends must be included in the rotation\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/KeyManagement/Validators/SendRotationValidator.cs","lineNumber":44,"sourceCode":"        _sendRepository = sendRepository;\n    }\n\n    public async Task<IReadOnlyList<Send>> ValidateAsync(User user, IEnumerable<SendWithIdRequestModel> sends)\n    {\n        var result = new List<Send>();\n\n        var existingSends = await _sendRepository.GetManyByUserIdAsync(user.Id);\n        if (existingSends == null || existingSends.Count == 0)\n        {\n            return result;\n        }\n\n        foreach (var existing in existingSends)\n        {\n            var send = sends.FirstOrDefault(c => c.Id == existing.Id);\n            if (send == null)\n            {\n                throw new BadRequestException(\"All existing sends must be included in the rotation.\");\n            }\n\n            result.Add(send.UpdateSend(existing, _sendAuthorizationService));\n        }\n\n        return result;\n    }\n}\n","sourceCodeStart":26,"sourceCodeEnd":53,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/KeyManagement/Validators/SendRotationValidator.cs#L26-L53","documentation":"Thrown by SendRotationValidator during key rotation. It loads every Bitwarden Send the user owns (each Send's name and data are encrypted with the user key) and requires the rotation request to include each one matched by Id. A missing Send would stay encrypted under the old key and become inaccessible, so the rotation is rejected.","triggerScenarios":"Key-rotation request whose sends array omits a Send.Id that exists for the user. A Send was created in another session after the client cached the list; the client sent a partial list; an Id was malformed.","commonSituations":"User created a Send on another device and then rotated keys here; client enumerated sends from local state that was stale; a sync race during rotation.","solutions":["Sync the vault/Sends so the client knows about all Sends immediately before building the rotation request.","Include every Send Id the user has, re-encrypted with the new key.","Delete any unwanted Sends before rotating rather than omitting them.","Validate the submitted Send Id set is a superset of the server set before sending."],"exampleFix":"// before\nconst sends = localSends.map(s => reencryptSend(s));\n\n// after\nawait syncSends();\nconst sends = allSends.map(s => reencryptSend(s));","handlingStrategy":"validation","validationCode":"const sends = await api.getSends();\nconst submitted = new Set(payload.sends.map(s => s.id));\nconst missing = sends.filter(s => !submitted.has(s.id));\nif (missing.length) {\n  throw new Error(`Rotation is missing sends: ${missing.map(s => s.id).join(', ')}`);\n}","typeGuard":"function isCompleteSendRotation(existing: { id: string }[], submitted: { id: string }[]): boolean {\n  const have = new Set(submitted.map(s => s.id));\n  return existing.every(s => have.has(s.id));\n}","tryCatchPattern":"try {\n  await api.rotateKey(payload);\n} catch (e) {\n  if (e.status === 400 && /sends must be included/i.test(e.message)) {\n    await syncSends();\n    payload.sends = allSends.map(s => reencryptSend(s));\n    return api.rotateKey(payload);\n  }\n  throw e;\n}","preventionTips":["Sync Sends immediately before constructing the rotation payload.","Delete unwanted Sends before rotating rather than omitting them.","Assert the submitted Send ids cover every server Send."],"tags":["key-rotation","send","encryption","validation","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}