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

  1. 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
  2. Restore the deleted user or their data from backup if the grantor should exist
  3. Reproduce via the emergency access API endpoints and check which record triggers it to purge precisely that row
  4. 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

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


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)