{"record":{"id":"c2250c60a951c931","repo":"astrid-runtime/astrid","slug":"mount-rollback-left-the-lease-registered-for-recov","errorCode":null,"errorMessage":"mount rollback left the lease registered for recovery; {}","messagePattern":"mount rollback left the lease registered for recovery; (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fskit/src/main.rs","lineNumber":340,"sourceCode":"async fn revoke_after_registry_failure(client: &mut AdminClient, mount_id: StorageMountId) {\n    let _ = client\n        .request(AdminRequestKind::StorageMountRevoke { mount_id })\n        .await;\n}\n\nfn with_native_rollback(\n    error: anyhow::Error,\n    rollback: Result<()>,\n) -> Result<StorageProviderSuccessV1> {\n    match rollback {\n        Ok(()) => Err(error),\n        Err(rollback) => Err(error).context(rollback),\n    }\n}\n\nfn rollback_outcome(native_unmounted: bool, errors: &[String]) -> Result<()> {\n    if !native_unmounted {\n        bail!(\n            \"mount rollback left the lease registered for recovery; {}\",\n            errors.join(\"; \")\n        );\n    }\n    if errors.is_empty() {\n        return Ok(());\n    }\n    bail!(\"mount rollback incomplete: {}\", errors.join(\"; \"))\n}\n\nasync fn rollback_after_native_failure(\n    client: &mut AdminClient,\n    mount_id: &StorageMountId,\n    mountpoint: &Path,\n    auto_created: bool,\n    native_mount_command_succeeded: bool,\n) -> Result<()> {\n    let mut errors = Vec::new();","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fskit/src/main.rs#L322-L358","documentation":"During rollback after a failed mount, rollback_after_native_failure could not unmount the native filesystem (native_unmounted == false), so the storage-mount lease remains registered in the kernel and only background recovery can clean it up. The bail reports the accumulated step errors (unmount/inspect) joined with '; '.","triggerScenarios":"rollback_after_native_failure() runs after a mount attempt fails partway; native_unmount(mountpoint) or native_mount_is_active(mountpoint) errors out (e.g. fskit process crashed, mount is busy, macOS refuses detach), so rollback_outcome(false, errors) bails.","commonSituations":"A file under the mount is open by another process, making unmount return EBUSY; fskit daemon died so the native mount is in an unknown state; unmount tooling unavailable mid-rollback.","solutions":["Free the mount: close processes holding files under the mountpoint, then unmount manually (e.g. diskutil unmount on macOS).","Check native_mount_is_active for the path; if still mounted, retry unmount until it succeeds, then revoke the lease via StorageMountRevoke.","Let kernel-side lease recovery expire the stale lease if manual unmount is impossible, then clean the registry entry.","Re-run the mount operation afterwards; stale recovery on next unmount will handle leftover leases if authorize_stale_cleanup permits it."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before mounting, ensure no process holds the mountpoint\nlet busy = std::process::Command::new(\"lsof\").arg(&mountpoint).status().map(|s| s.success()).unwrap_or(false);\nif busy { return Err(anyhow!(\"mountpoint is busy; aborting mount\")); }","typeGuard":null,"tryCatchPattern":"match rollback_after_native_failure(&mut client, &mount_id, &mountpoint, auto_created, mounted).await {\n    Err(e) if e.to_string().contains(\"left the lease registered for recovery\") => {\n        // retry native unmount after releasing busy resources, then revoke lease explicitly\n    },\n    other => { /* proceed */ },\n}","preventionTips":["Ensure no open file handles or watchers under the mountpoint before mount/unmount.","Monitor the fskit daemon so it does not die mid-operation.","After this error, run manual unmount + StorageMountRevoke to converge state.","Rely on the registry's stale-recovery path for leases you cannot revoke immediately."],"tags":["rollback","unmount","lease","busy-mount"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}