astrid-runtime/astrid · error

WinFsp service parent process is not alive

Error message

WinFsp service parent process is not alive

What it means

run_private_service starts by checking the parent process recorded in the launch (launch.parent) via parent_is_alive. If the parent has exited, the service bails: continuing would leave an orphaned privileged process with no one to own the control channel or reap the result. The check is a liveness handshake between parent and child.

Source

Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:197

        &launch.lease.callback_path,
    )
    .map_err(anyhow::Error::msg)?;
    let runtime = Arc::new(
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .context("start WinFsp service runtime")?,
    );
    runtime.block_on(run_private_service(launch, challenge, runtime.clone()))
}

async fn run_private_service(
    launch: StorageProviderServiceLaunchV1,
    challenge: String,
    runtime: Arc<tokio::runtime::Runtime>,
) -> Result<()> {
    if !parent_is_alive(&launch.parent) {
        bail!("WinFsp service parent process is not alive");
    }
    probe_callback(&launch).await?;
    let listener = local_transport::bind(&launch.control_path).with_context(|| {
        format!(
            "bind WinFsp service control {}",
            launch.control_path.display()
        )
    })?;
    let callback = CallbackFs::new(launch.lease.clone(), runtime)
        .map_err(|failure| anyhow::anyhow!("build WinFsp callback filesystem: {failure:?}"))?;
    initialize_winfsp()?;
    let mountpoint = U16CString::from_os_str(launch.mountpoint.as_os_str())
        .map_err(|_| anyhow::anyhow!("WinFsp mountpoint is not valid UTF-16"))?;
    let filesystem = FileSystem::start(
        volume_params(launch.lease.access),
        Some(&mountpoint),
        callback,
    )

View on GitHub (pinned to affd8760f4)

Solutions

  1. Ensure the parent stays alive for the whole child startup handshake (wait for the ready challenge before exiting)
  2. Log and retry the launch if the parent was terminated, regenerating the launch document with a fresh live PID
  3. Verify parent_is_alive's inputs: the PID/token in the launch must refer to the actual launching process
  4. If the parent intentionally spawns and exits, restructure to keep a supervisor process alive instead

Example fix

// before
spawn_service(&launch)?;
std::process::exit(0); // parent gone before child validates liveness
// after
spawn_service(&launch)?;
wait_for_service_ready(&launch); // parent stays alive through the handshake
Defensive patterns

Strategy: try-catch

Validate before calling

// parent side: confirm your own process is alive and the PID recorded is current
assert_eq!(launch.parent.pid, std::process::id(), "launch must record the live parent PID");

Type guard

fn launch_parent_is_self(launch: &StorageProviderServiceLaunchV1) -> bool {
    launch.parent.pid == std::process::id()
}

Try / catch

match service_err {
    Err(e) if e.to_string().contains("parent process is not alive") => {
        eprintln!("parent exited during handshake; relaunch with a live supervisor");
        relaunch_with_supervisor()?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: The parent process crashes or exits between writing the launch document and the service reading it; the launch embeds a stale parent PID/token from a previous run; the wrong PID was serialized into launch.parent.

Common situations: Parent killed by a timeout runner or Ctrl+C during startup; services launched by a short-lived bootstrap script that exits immediately; PID reuse pointing at a different (dead) process; relaunching a stale launch file.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/4b36ef072a34e253. Report an issue: GitHub.