{"record":{"id":"1554d7328640e3eb","repo":"astrid-runtime/astrid","slug":"fuse-service-unmount-failed-code-message","errorCode":null,"errorMessage":"FUSE service unmount failed [{code}]: {message}","messagePattern":"FUSE service unmount failed \\[(.+?)\\]: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fuse/src/main.rs","lineNumber":875,"sourceCode":"    registry::remove_record(&record.mount_id)?;\n    cleanup_service_artifacts(\n        &record.control_path,\n        &record.mountpoint,\n        record.auto_created_mountpoint,\n    )?;\n    Ok(())\n}\n\nfn control_unmount(control_path: &Path, acting_principal: &astrid_core::PrincipalId) -> Result<()> {\n    match call_control(\n        control_path,\n        &ControlRequest::Unmount {\n            requested_by: acting_principal.clone(),\n        },\n    )? {\n        ControlResponse::Done => Ok(()),\n        ControlResponse::Failure { code, message } => {\n            bail!(\"FUSE service unmount failed [{code}]: {message}\")\n        },\n        ControlResponse::Status { .. } => {\n            bail!(\"FUSE service returned an incompatible unmount response\")\n        },\n    }\n}\n\nfn cleanup_mountpoint(mountpoint: &Path, auto_created: bool) -> Result<()> {\n    if auto_created\n        && !mountpoint::mountinfo_contains(mountpoint)?\n        && std::fs::symlink_metadata(mountpoint).is_ok_and(|metadata| metadata.is_dir())\n        && std::fs::read_dir(mountpoint)?.next().is_none()\n    {\n        let _ = std::fs::remove_dir(mountpoint);\n    }\n    Ok(())\n}\n","sourceCodeStart":857,"sourceCodeEnd":893,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fuse/src/main.rs#L857-L893","documentation":"When unmounting, the FUSE control service can reject the request with a ControlResponse::Failure carrying a code and message. The client propagates that failure as this bail in crates/astrid-storage-provider-fuse/src/main.rs:875, embedding the service-side code and message.","triggerScenarios":"Calling the unmount path while the FUSE service refuses the Unmount request — e.g. the mountpoint is still busy, the requesting principal (requested_by) is not authorized, or the service has no active mount.","commonSituations":"Open file handles keep the mount busy; a different user tries to unmount another principal's mount; the kernel-level FUSE mount already died but the service still tracks it.","solutions":["Read the embedded [code] and {message} from the error to identify the service-side cause and fix that first (e.g. close files, correct principal)","Retry the unmount after ensuring no processes hold open files under the mountpoint (fuser -v <mountpoint>)","If the service is stale, force-terminate the FUSE daemon and clean up the mountpoint/record manually, then remount cleanly"],"exampleFix":"// before\nunmount(...)?; // FUSE service unmount failed [EBUSY]: device busy\n// after\nensure_no_open_handles(mountpoint)?; // e.g. run fuser/lsof check\nunmount(...)?;","handlingStrategy":"try-catch","validationCode":"if has_open_handles(mountpoint) { bail!(\"close handles before unmount\") }","typeGuard":"fn is_failure(resp: &ControlResponse) -> Option<(u32, String)> { match resp { ControlResponse::Failure { code, message } => Some((*code, message.clone())), _ => None } }","tryCatchPattern":"match unmount(...) { Err(e) if e.to_string().starts_with(\"FUSE service unmount failed\") => { log_service_code(&e); handle_service_failure(&e) }, r => r? }","preventionTips":["Close all files under the mountpoint before unmounting","Unmount with the same principal that mounted","Check service logs for the embedded failure code"],"tags":["fuse","unmount","control-protocol","service-failure"],"backgroundTag":"api-error-response","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}