{"record":{"id":"4ab79f7df748500b","repo":"dani-garcia/vaultwarden","slug":"grantee-user-should-exist-but-does-not","errorCode":null,"errorMessage":"Grantee user should exist but does not!","messagePattern":"Grantee user should exist but does not!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/api/core/emergency_access.rs","lineNumber":96,"sourceCode":"        emergency_access_list_json.push(ea.to_json_grantor_details(&conn).await);\n    }\n\n    Json(json!({\n      \"data\": emergency_access_list_json,\n      \"object\": \"list\",\n      \"continuationToken\": null\n    }))\n}\n\n#[get(\"/emergency-access/<emer_id>\")]\nasync fn get_emergency_access(emer_id: EmergencyAccessId, headers: Headers, conn: DbConn) -> JsonResult {\n    check_emergency_access_enabled()?;\n\n    if let Some(emergency_access) =\n        EmergencyAccess::find_by_uuid_and_grantor_uuid(&emer_id, &headers.user.uuid, &conn).await\n    {\n        Ok(Json(\n            emergency_access.to_json_grantee_details(&conn).await.expect(\"Grantee user should exist but does not!\"),\n        ))\n    } else {\n        err!(\"Emergency access not valid.\")\n    }\n}\n\n// endregion\n\n// region put/post\n\n#[derive(Deserialize)]\n#[serde(rename_all = \"camelCase\")]\nstruct EmergencyAccessUpdateData {\n    r#type: NumberOrString,\n    wait_time_days: i32,\n    key_encrypted: Option<String>,\n}\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/0cefa4cca7c9f2a5579dd290f78193b543818c51/src/api/core/emergency_access.rs#L78-L114","documentation":"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.","triggerScenarios":"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.","commonSituations":"Admin-deleted or purged accounts with lingering emergency relationships; databases restored partially; compliance purges that remove users but not dependent rows.","solutions":["Grantor deletes the orphaned entry via DELETE /emergency-access/{id} (which does not render grantee details)","Or purge orphaned rows in the DB directly (back up first)","Re-verify remaining emergency access entries afterwards","Code fix: handle the None case with an err!() instead of expect"],"exampleFix":"// before\nemergency_access.to_json_grantee_details(&conn).await.expect(\"Grantee user should exist but does not!\")\n// after\nmatch emergency_access.to_json_grantee_details(&conn).await {\n    Some(details) => Ok(Json(details)),\n    None => err!(\"Grantee user for this emergency access no longer exists\"),\n}","handlingStrategy":"validation","validationCode":"-- Find emergency access rows whose grantee is missing before users hit the endpoint\nSELECT ea.uuid, ea.grantor_uuid, ea.grantee_uuid, ea.email\nFROM emergency_access ea\nLEFT JOIN users u ON u.uuid = ea.grantee_uuid\nWHERE ea.grantee_uuid IS NOT NULL AND u.uuid IS NULL;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Delete or decline emergency accesses before deleting user accounts","Run the orphan-check query after user purges","Back up before manual deletes on emergency_access"],"tags":["rust","emergency-access","database","panic","data-integrity"],"backgroundTag":null,"analyzedSha":"0cefa4cca7c9f2a5579dd290f78193b543818c51","analyzedAt":"2026-08-16T07:44:56.102Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}