dani-garcia/vaultwarden · error
Error getting grantee
Error message
Error getting grantee
What it means
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.
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
Example fix
// before
User::update_uuid_revision(self.grantee_uuid.as_ref().expect("Error getting grantee"), conn).await;
// after
let grantee = self.grantee_uuid.as_ref().ok_or_else(|| err!("Error getting grantee"))?;
User::update_uuid_revision(grantee, conn).await; Defensive patterns
Strategy: validation
Validate before calling
// before updating, require a confirmed record with a grantee // if ea.grantee_uuid is None or ea.status != Confirmed -> do not call update_access_status_and_save
Try / catch
// Guard the call site
if record.grantee_uuid.is_some() { record.update_access_status_and_save(...).await?; } else { return Err(invalid_state); } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Error loading emergency_access
- Error on sending email
- Grantee user invalid
- Grantee user not found
- Grantee user not found.
AI-assisted analysis of dani-garcia/vaultwarden@cc67d644f6 (2026-09-19).
Data as JSON: /api/errors/77d03b0430955d92.
Report an issue: GitHub.
Appendix: source
Thrown at src/db/models/emergency_access.rs:177
diesel::insert_into(emergency_access::table)
.values(&*self)
.on_conflict(emergency_access::uuid)
.do_update()
.set(&*self)
.execute(conn)
.map_res("Error saving emergency access")
}
}
}
pub async fn update_access_status_and_save(
&mut self,
status: i32,
date: &NaiveDateTime,
conn: &DbConn,
) -> EmptyResult {
// Update the grantee so that it will refresh it's status.
User::update_uuid_revision(self.grantee_uuid.as_ref().expect("Error getting grantee"), conn).await;
self.status = status;
date.clone_into(&mut self.updated_at);
conn.run(move |conn| {
crate::util::retry(
|| {
diesel::update(emergency_access::table.filter(emergency_access::uuid.eq(&self.uuid)))
.set((emergency_access::status.eq(status), emergency_access::updated_at.eq(date)))
.execute(conn)
},
10,
)
.map_res("Error updating emergency access status")
})
.await
}
pub async fn update_last_notification_date_and_save(&mut self, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult {View on GitHub (pinned to cc67d644f6)