dani-garcia/vaultwarden · error

Grantor user not found

Error message

Grantor user not found

What it means

In the same timeout job with mail enabled, the grantor user is loaded with User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found") to address the notification email. If the grantor account was deleted while the emergency_access row survived, the lookup returns None and the job task panics, stopping the job for all remaining rows until restart.

Source

Thrown at src/api/core/emergency_access.rs:750

            debug!("No emergency request timeout to approve");
        }

        let now = Utc::now().naive_utc();
        for mut emer in emergency_access_list {
            // The find_all_recoveries_initiated already checks if the recovery_initiated_at is not null (None)
            let recovery_allowed_at =
                emer.recovery_initiated_at.unwrap() + TimeDelta::try_days(i64::from(emer.wait_time_days)).unwrap();
            if recovery_allowed_at.le(&now) {
                // Only update the access status
                // Updating the whole record could cause issues when the emergency_notification_reminder_job is also active
                emer.update_access_status_and_save(EmergencyAccessStatus::RecoveryApproved as i32, &now, &conn)
                    .await
                    .expect("Unable to update emergency access status");

                if CONFIG.mail_enabled() {
                    // get grantor user to send Accepted email
                    let grantor_user =
                        User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found");

                    // get grantee user to send Accepted email
                    let grantee_user =
                        User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &conn)
                            .await
                            .expect("Grantee user not found");

                    mail::send_emergency_access_recovery_timed_out(
                        &grantor_user.email,
                        &grantee_user.name,
                        emer.get_type_as_str(),
                    )
                    .await
                    .expect("Error on sending email");

                    mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name)
                        .await
                        .expect("Error on sending email");

View on GitHub (pinned to 0cefa4cca7)

Solutions

  1. Purge or fix orphaned rows (back up first): DELETE FROM emergency_access WHERE grantor_uuid NOT IN (SELECT uuid FROM users)
  2. Restart Vaultwarden to restart the job task
  3. Code fix: treat a missing user as a skip, not a panic

Example fix

// before
let grantor_user = User::find_by_uuid(&emer.grantor_uuid, &conn).await.expect("Grantor user not found");
// after
let Some(grantor_user) = User::find_by_uuid(&emer.grantor_uuid, &conn).await else {
    warn!("Grantor {} missing for emergency access {}; skipping", emer.grantor_uuid, emer.uuid);
    continue;
};
Defensive patterns

Strategy: validation

Validate before calling

-- Detect grantor orphans before the job does
SELECT ea.uuid, ea.grantor_uuid FROM emergency_access ea
LEFT JOIN users u ON u.uuid = ea.grantor_uuid
WHERE u.uuid IS NULL;

Prevention

When it happens

Trigger: A recovery timing out for an emergency access whose grantor_uuid no longer matches a user row — deletion without cleanup, corrupted uuid values, or partial restores.

Common situations: Admin-deleted or purged accounts with lingering emergency access; database restores that dropped users but kept emergency_access rows.

Related errors


AI-assisted analysis of dani-garcia/vaultwarden@0cefa4cca7 (2026-08-16). Data as JSON: /api/errors/110167e934f7c5c8. Report an issue: GitHub.