dani-garcia/vaultwarden · error
Grantor user not found.
Error message
Grantor user not found.
What it means
Panic from `.expect()` in `EmergencyAccess::to_json_grantor_details` when `User::find_by_uuid(&self.grantor_uuid)` returns None. The code assumes every emergency-access row references an existing grantor user; if that user was deleted (or the row is stale/orphaned), serialization of the emergency access JSON panics.
Solutions
- Delete the orphaned emergency_access rows referencing the missing grantor: SELECT * FROM emergency_access WHERE grantor_uuid NOT IN (SELECT uuid FROM users); then DELETE them
- Restore the deleted user or their data from backup if the grantor should exist
- Reproduce via the emergency access API endpoints and check which record triggers it to purge precisely that row
- Harden the code to return an error/Option instead of expect() when the grantor is missing
Example fix
// before
User::find_by_uuid(&self.grantor_uuid, conn).await.expect("Grantor user not found.")
// after
User::find_by_uuid(&self.grantor_uuid, conn).await.ok_or_else(|| err!("Grantor user not found."))? Defensive patterns
Strategy: validation
Validate before calling
// before serializing, confirm the grantor exists SELECT 1 FROM users u JOIN emergency_access ea ON ea.grantor_uuid = u.uuid WHERE ea.uuid = ?;
Try / catch
// Catch the panic at the API boundary and return a 404/500 with a logged orphaned-record warning res = catch_unwind(|| to_json_grantor_details(ea)).unwrap_or_else(|_| err_not_found());
Prevention
- Purge emergency_access rows when deleting users (cascade cleanup)
- Audit for orphaned grantor_uuid values after restores or imports
- Back up before user deletions so recovery is possible
- Regularly run consistency queries: emergency_access.grantor_uuid NOT IN (SELECT uuid FROM users)
When it happens
Trigger: Serializing an emergency_access record whose grantor user no longer exists — e.g. the grantor account was deleted/purged while the emergency_access row remained, or a manually edited/imported DB left an orphaned grantor_uuid.
Common situations: Admin deleting a user without cleaning up their emergency access rows; restoring a partial database backup; invitation rows left over from removed accounts when listing emergency access in the API.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Grantee user not found.
- Error loading emergency_access
- Grantee user invalid
- Grantee user not found
- Grantee user should exist but does not!
AI-assisted analysis of dani-garcia/vaultwarden@cc67d644f6 (2026-09-19).
Data as JSON: /api/errors/133124d7c2581d41.
Report an issue: GitHub.
Appendix: source
Thrown at src/db/models/emergency_access.rs:74
if self.atype == EmergencyAccessType::View as i32 {
"View"
} else {
"Takeover"
}
}
pub fn to_json(&self) -> Value {
json!({
"id": self.uuid,
"status": self.status,
"type": self.atype,
"waitTimeDays": self.wait_time_days,
"object": "emergencyAccess",
})
}
pub async fn to_json_grantor_details(&self, conn: &DbConn) -> Value {
let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).await.expect("Grantor user not found.");
json!({
"id": self.uuid,
"status": self.status,
"type": self.atype,
"waitTimeDays": self.wait_time_days,
"grantorId": grantor_user.uuid,
"email": grantor_user.email,
"name": grantor_user.name,
"avatarColor": grantor_user.avatar_color,
"object": "emergencyAccessGrantorDetails",
})
}
pub async fn to_json_grantee_details(&self, conn: &DbConn) -> Option<Value> {
let grantee_user = if let Some(grantee_uuid) = &self.grantee_uuid {
User::find_by_uuid(grantee_uuid, conn).await.expect("Grantee user not found.")
} else {View on GitHub (pinned to cc67d644f6)