{"record":{"id":"2e5f1dd6612ba48b","repo":"bitwarden/server","slug":"emergency-access-keys-cannot-be-set-to-null-during","errorCode":null,"errorMessage":"Emergency access keys cannot be set to null during rotation.","messagePattern":"Emergency access keys cannot be set to null during rotation\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/KeyManagement/Validators/EmergencyAccessRotationValidator.cs","lineNumber":46,"sourceCode":"        var existing = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(user.Id);\n        if (existing == null || existing.Count == 0)\n        {\n            return result;\n        }\n        // Exclude any emergency access that has not been confirmed yet.\n        existing = existing.Where(ea => ea.KeyEncrypted != null).ToList();\n\n        foreach (var ea in existing)\n        {\n            var emergencyAccess = emergencyAccessKeys.FirstOrDefault(c => c.Id == ea.Id);\n            if (emergencyAccess == null)\n            {\n                throw new BadRequestException(\"All existing emergency access keys must be included in the rotation.\");\n            }\n\n            if (emergencyAccess.KeyEncrypted == null)\n            {\n                throw new BadRequestException(\"Emergency access keys cannot be set to null during rotation.\");\n            }\n\n            result.Add(emergencyAccess.ToEmergencyAccess(ea));\n        }\n\n        return result;\n    }\n}\n","sourceCodeStart":28,"sourceCodeEnd":55,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/KeyManagement/Validators/EmergencyAccessRotationValidator.cs#L28-L55","documentation":"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.","triggerScenarios":"The emergency-access array includes the grant Id but the KeyEncrypted field was left null or stripped out during request construction.","commonSituations":"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.","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."],"exampleFix":"// before: trying to drop a grant by nulling\n{ id: g.id, keyEncrypted: null }\n\n// after: revoke separately, then rotate the rest\nawait api.revokeEmergencyAccess(g.id);\nrotate({ emergencyAccessKeys: remaining.map(g => ({ id: g.id, keyEncrypted: reencrypt(g.key) })) });","handlingStrategy":"validation","validationCode":"const nulled = payload.emergencyAccessKeys.filter(g => g.keyEncrypted == null);\nif (nulled.length) {\n  throw new Error(`Emergency access keys cannot be null: ${nulled.map(g => g.id).join(', ')}`);\n}","typeGuard":"function hasEmergencyAccessKey(g: { keyEncrypted?: string | null }): boolean {\n  return g.keyEncrypted != null && g.keyEncrypted.length > 0;\n}","tryCatchPattern":"try {\n  await api.rotateKey(payload);\n} catch (e) {\n  if (e.status === 400 && /cannot be set to null/i.test(e.message)) {\n    payload.emergencyAccessKeys = payload.emergencyAccessKeys.map(g => ({ ...g, keyEncrypted: reencrypt(g.keyEncrypted ?? oldKeyFor(g.id)) }));\n    return api.rotateKey(payload);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["key-rotation","emergency-access","null-check","encryption","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}