{"record":{"id":"77d03b0430955d92","repo":"dani-garcia/vaultwarden","slug":"error-getting-grantee","errorCode":null,"errorMessage":"Error getting grantee","messagePattern":"Error getting grantee","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/db/models/emergency_access.rs","lineNumber":177,"sourceCode":"                diesel::insert_into(emergency_access::table)\n                    .values(&*self)\n                    .on_conflict(emergency_access::uuid)\n                    .do_update()\n                    .set(&*self)\n                    .execute(conn)\n                    .map_res(\"Error saving emergency access\")\n            }\n        }\n    }\n\n    pub async fn update_access_status_and_save(\n        &mut self,\n        status: i32,\n        date: &NaiveDateTime,\n        conn: &DbConn,\n    ) -> EmptyResult {\n        // Update the grantee so that it will refresh it's status.\n        User::update_uuid_revision(self.grantee_uuid.as_ref().expect(\"Error getting grantee\"), conn).await;\n        self.status = status;\n        date.clone_into(&mut self.updated_at);\n\n        conn.run(move |conn| {\n            crate::util::retry(\n                || {\n                    diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid)))\n                        .set((emergency_access::status.eq(status), emergency_access::updated_at.eq(date)))\n                        .execute(conn)\n                },\n                10,\n            )\n            .map_res(\"Error updating emergency access status\")\n        })\n        .await\n    }\n\n    pub async fn update_last_notification_date_and_save(&mut self, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/cc67d644f62605cb46f4d16c4a2eed1a861cc8bb/src/db/models/emergency_access.rs#L159-L195","documentation":"Panic from `.expect()` in `EmergencyAccess::update_access_status_and_save` when `self.grantee_uuid` is None. Only confirmed emergency accesses have a grantee; calling this status-update method on an invited/pending record (grantee_uuid is NULL) violates the invariant and panics before touching the database.","triggerScenarios":"Calling `update_access_status_and_save` on an EmergencyAccess whose grantee_uuid is None — e.g. accepting/confirming a recovery on an invitation row that was never linked to a grantee user, or a race where the grantee was removed first.","commonSituations":"Client/API calling the emergency-access accept or status endpoint twice or on an already-removed grantee; concurrent grantee deletion during status update; data imported from backup with NULL grantee_uuid on records assumed confirmed.","solutions":["Ensure the record is in Confirmed state (grantee_uuid set) before calling update_access_status_and_save; check status first","Clean up emergency_access rows with NULL grantee_uuid that should not exist","Guard the method: return an error instead of expect when grantee_uuid is None","Check for concurrent deletions — re-fetch the record and verify grantee before updating"],"exampleFix":"// before\nUser::update_uuid_revision(self.grantee_uuid.as_ref().expect(\"Error getting grantee\"), conn).await;\n// after\nlet grantee = self.grantee_uuid.as_ref().ok_or_else(|| err!(\"Error getting grantee\"))?;\nUser::update_uuid_revision(grantee, conn).await;","handlingStrategy":"validation","validationCode":"// before updating, require a confirmed record with a grantee\n// if ea.grantee_uuid is None or ea.status != Confirmed -> do not call update_access_status_and_save","typeGuard":null,"tryCatchPattern":"// Guard the call site\nif record.grantee_uuid.is_some() { record.update_access_status_and_save(...).await?; } else { return Err(invalid_state); }","preventionTips":["Only call status updates on Confirmed emergency-access records","Re-fetch the record before mutating to avoid races with grantee deletion","Add a NOT NULL-with-status invariant check in tests for grantee_uuid","Handle double-accept/confirm API calls idempotently"],"tags":["null-value","invariant-violation","panic","emergency-access"],"backgroundTag":"null-argument","analyzedSha":"cc67d644f62605cb46f4d16c4a2eed1a861cc8bb","analyzedAt":"2026-09-19T08:28:46.370Z","contentChangedAt":"2026-09-19T08:28:46.370Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}