{"record":{"id":"02c02e2180ac5642","repo":"bitwarden/server","slug":"all-existing-folders-must-be-included-in-the-rotat","errorCode":null,"errorMessage":"All existing folders must be included in the rotation.","messagePattern":"All existing folders must be included in the rotation\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/KeyManagement/Validators/FolderRotationValidator.cs","lineNumber":33,"sourceCode":"        _folderRepository = folderRepository;\n    }\n\n    public async Task<IEnumerable<Folder>> ValidateAsync(User user, IEnumerable<FolderWithIdRequestModel> folders)\n    {\n        var result = new List<Folder>();\n\n        var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);\n        if (existingFolders == null || existingFolders.Count == 0)\n        {\n            return result;\n        }\n\n        foreach (var existing in existingFolders)\n        {\n            var folder = folders.FirstOrDefault(c => c.Id == existing.Id);\n            if (folder == null)\n            {\n                throw new BadRequestException(\"All existing folders must be included in the rotation.\");\n            }\n            result.Add(folder.ToFolder(existing));\n        }\n        return result;\n    }\n}\n","sourceCodeStart":15,"sourceCodeEnd":40,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/KeyManagement/Validators/FolderRotationValidator.cs#L15-L40","documentation":"Thrown by FolderRotationValidator during key rotation. It loads every folder the user owns and requires the rotation request to include each one matched by Id, because each folder's name is encrypted with the user key. A missing folder would remain under the old key and become unreadable, so the whole rotation is rejected.","triggerScenarios":"Key-rotation request whose folders array omits a Folder.Id that exists for the user. A folder was created in another session after the client cached the folder list; the client sent a partial list; an Id was malformed.","commonSituations":"User created a folder on another device/client and then rotated keys here; client enumerated folders from local vault state that was out of sync; a sync race during rotation.","solutions":["Sync/refresh the vault so the client knows about all folders immediately before building the rotation request.","Include every folder Id the user has, re-encrypted with the new key.","Delete any unwanted folders before rotating rather than omitting them from the rotation list.","Validate the submitted folder Id set is a superset of the server folder set before sending."],"exampleFix":"// before\nconst folders = localVault.folders.map(f => ({ id: f.id, name: reencrypt(f.name) }));\n\n// after\nawait vault.sync();\nconst folders = vault.folders.map(f => ({ id: f.id, name: reencrypt(f.name) }));","handlingStrategy":"validation","validationCode":"const folders = await api.getFolders();\nconst submitted = new Set(payload.folders.map(f => f.id));\nconst missing = folders.filter(f => !submitted.has(f.id));\nif (missing.length) {\n  throw new Error(`Rotation is missing folders: ${missing.map(f => f.id).join(', ')}`);\n}","typeGuard":"function isCompleteFolderRotation(existing: { id: string }[], submitted: { id: string }[]): boolean {\n  const have = new Set(submitted.map(s => s.id));\n  return existing.every(f => have.has(f.id));\n}","tryCatchPattern":"try {\n  await api.rotateKey(payload);\n} catch (e) {\n  if (e.status === 400 && /folders must be included/i.test(e.message)) {\n    await vault.sync();\n    payload.folders = vault.folders.map(f => ({ id: f.id, name: reencrypt(f.name) }));\n    return api.rotateKey(payload);\n  }\n  throw e;\n}","preventionTips":["Sync the vault immediately before constructing the rotation payload.","Delete unwanted folders before rotating rather than omitting them.","Assert the submitted folder ids cover every server folder."],"tags":["key-rotation","folder","encryption","validation","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}