dani-garcia/vaultwarden · error

Grantee user should exist but does not!

Error message

Grantee user should exist but does not!

What it means

GET /emergency-access/{id} (grantor viewing details): the record is found for the grantor, then to_json_grantee_details() resolves the grantee user and the caller unwraps it with .expect("Grantee user should exist but does not!"). If the grantee account was deleted without cleaning the emergency_access row, this ordinary read endpoint panics (surfaced as a 500/connection reset) instead of returning a graceful error.

Source

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

        emergency_access_list_json.push(ea.to_json_grantor_details(&conn).await);
    }

    Json(json!({
      "data": emergency_access_list_json,
      "object": "list",
      "continuationToken": null
    }))
}

#[get("/emergency-access/<emer_id>")]
async fn get_emergency_access(emer_id: EmergencyAccessId, headers: Headers, conn: DbConn) -> JsonResult {
    check_emergency_access_enabled()?;

    if let Some(emergency_access) =
        EmergencyAccess::find_by_uuid_and_grantor_uuid(&emer_id, &headers.user.uuid, &conn).await
    {
        Ok(Json(
            emergency_access.to_json_grantee_details(&conn).await.expect("Grantee user should exist but does not!"),
        ))
    } else {
        err!("Emergency access not valid.")
    }
}

// endregion

// region put/post

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct EmergencyAccessUpdateData {
    r#type: NumberOrString,
    wait_time_days: i32,
    key_encrypted: Option<String>,
}

View on GitHub (pinned to 0cefa4cca7)

Solutions

  1. Grantor deletes the orphaned entry via DELETE /emergency-access/{id} (which does not render grantee details)
  2. Or purge orphaned rows in the DB directly (back up first)
  3. Re-verify remaining emergency access entries afterwards
  4. Code fix: handle the None case with an err!() instead of expect

Example fix

// before
emergency_access.to_json_grantee_details(&conn).await.expect("Grantee user should exist but does not!")
// after
match emergency_access.to_json_grantee_details(&conn).await {
    Some(details) => Ok(Json(details)),
    None => err!("Grantee user for this emergency access no longer exists"),
}
Defensive patterns

Strategy: validation

Validate before calling

-- Find emergency access rows whose grantee is missing before users hit the endpoint
SELECT ea.uuid, ea.grantor_uuid, ea.grantee_uuid, ea.email
FROM emergency_access ea
LEFT JOIN users u ON u.uuid = ea.grantee_uuid
WHERE ea.grantee_uuid IS NOT NULL AND u.uuid IS NULL;

Prevention

When it happens

Trigger: Grantor opens the emergency access details page or calls GET /emergency-access/{id} for an entry whose grantee user no longer exists — account deleted after invite/acceptance, or grantee_uuid/email dangling due to manual DB edits or partial restores.

Common situations: Admin-deleted or purged accounts with lingering emergency relationships; databases restored partially; compliance purges that remove users but not dependent rows.

Related errors


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