{"record":{"id":"ba1e9fd434761d5c","repo":"astrid-runtime/astrid","slug":"fuse-service-parent-process-is-not-alive","errorCode":null,"errorMessage":"FUSE service parent process is not alive","messagePattern":"FUSE service parent process is not alive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fuse/src/service.rs","lineNumber":61,"sourceCode":"}\n\nasync fn run_launch(launch: StorageProviderServiceLaunchV1) -> Result<()> {\n    if launch.schema != STORAGE_FILESYSTEM_SERVICE_LAUNCH_SCHEMA_V1 {\n        bail!(\"unsupported FUSE service launch schema {}\", launch.schema);\n    }\n    validate_launch(&launch)?;\n    let challenge = storage_provider_service_ready_challenge(\n        &launch.parent.token,\n        STORAGE_FILESYSTEM_SERVICE_READY_SCHEMA_V1,\n        crate::PROVIDER_NAME,\n        launch.lease.mount_id.as_uuid(),\n        &launch.control_path,\n        &launch.lease.resource_path,\n        &launch.lease.callback_path,\n    )\n    .map_err(|error| anyhow::anyhow!(error))?;\n    if !parent_is_alive(&launch.parent) {\n        bail!(\"FUSE service parent process is not alive\");\n    }\n    probe_callback(&launch).await?;\n    let listener = bind_control_listener(&launch.control_path)?;\n    let mut session = match filesystem::start_session(launch.lease.clone(), &launch.mountpoint) {\n        Ok(session) => Some(session),\n        Err(error) => {\n            let _ = std::fs::remove_file(&launch.control_path);\n            return Err(error);\n        },\n    };\n    let ready = StorageProviderServiceReadyV1 {\n        schema: STORAGE_FILESYSTEM_SERVICE_READY_SCHEMA_V1,\n        provider: crate::PROVIDER_NAME.to_owned(),\n        mount_id: launch.lease.mount_id.as_uuid(),\n        control_path: launch.control_path.clone(),\n        challenge,\n    };\n    let mut stdout = std::io::stdout().lock();","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fuse/src/service.rs#L43-L79","documentation":"The FUSE service helper process checks that its parent (the mounting client process) is still alive before proceeding with the mount (binding the control listener and starting the filesystem session). If the parent PID no longer exists, the helper bails out instead of serving an orphaned mount. This prevents a leaked FUSE mount whose controller has died.","triggerScenarios":"`run_launch` (invoked by the service `run` loop) calls `parent_is_alive(&launch.parent)` right after opening control/resource/callback paths and it returns false — the parent process identified by `launch.parent.pid` has exited between spawn and the liveness check.","commonSituations":"The mounting CLI was killed or crashed while the FUSE helper was starting; the parent exited due to an earlier error while the helper was still initializing; PID reuse in containers makes the check race; slow startup (network mounts) gives the parent time to be terminated by a supervisor.","solutions":["Re-run the mount operation so a live parent spawns a fresh FUSE service process.","Check whether the parent process (the CLI/daemon performing the mount) crashed and fix its startup error before retrying.","Reduce work done by the parent between spawning the helper and the mount, or ensure the parent waits for the helper's ready signal.","Inspect supervisor/timeout settings (systemd, docker) that may be killing the parent mid-launch.","If PID reuse is suspected in long-lived containers, use the parent token handshake rather than assuming a stale PID."],"exampleFix":"// before: parent dies during startup, helper bails\nspawn_helper(...);\nheavy_init(); // may exit; helper sees dead parent\n// after: finish init before spawning, or keep parent alive until ready\nheavy_init();\nspawn_helper(...);\nwait_for_helper_ready();","handlingStrategy":"try-catch","validationCode":"if !parent_is_alive(&parent) {\n    // refresh spawn / abort before calling run\n}\nfn parent_is_alive(parent: &StorageProviderParentLifetimeV1) -> bool {\n    unsafe { libc::kill(parent.pid as i32, 0) == 0 }\n}","typeGuard":"fn parent_alive(pid: u32) -> bool {\n    std::process::Command::new(\"kill\").arg(\"-0\").arg(pid.to_string()).status().map(|s| s.success()).unwrap_or(false)\n}","tryCatchPattern":"match service.run().await {\n    Err(e) if e.to_string().contains(\"parent process is not alive\") => restart_mount_with_live_parent(),\n    Err(e) => return Err(e),\n    Ok(v) => Ok(v),\n}","preventionTips":["Keep the parent alive until the helper signals readiness","Fix parent startup crashes first, then retry the mount","Avoid supervisors that kill the parent mid-launch","Don't rely on PID reuse in containers"],"tags":["fuse","process-lifecycle","ipc"],"backgroundTag":"parent-process-exited","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}