astrid-runtime/astrid · error

detached FUSE service returned an invalid process identity

Error message

detached FUSE service returned an invalid process identity

What it means

The parent validates the pid returned in ServiceStartup::Ready; pid == 0 is not a valid OS process identity (pid 0 is the idle/scheduler). If the service reports a zero pid, the handoff contract is broken and the parent cannot supervise or signal the daemon, so it rejects the startup.

Solutions

  1. Fix the service to populate pid with std::process::id() (after any daemonize/fork step) before sending Ready
  2. Log the pid at the service just before sending Ready and compare with the parent's log
  3. If the service double-forks, report the pid of the process that will keep running, not the intermediate one
  4. Check the ServiceStartup serialization: ensure the pid field isn't skipped/defaulted to 0

Example fix

// before
let startup = ServiceStartup::Ready { mount_id, pid: 0, access };
// after
let startup = ServiceStartup::Ready { mount_id, pid: std::process::id(), access };
Defensive patterns

Strategy: validation

Validate before calling

// Caller-side sanity check on the returned ControlReady
if ready.pid == 0 {
    return Err(anyhow!("service reported invalid pid"));
}
if !pid_exists(ready.pid) {
    return Err(anyhow!("service pid {} is not a live process", ready.pid));
}

Type guard

fn valid_pid(ready: &ControlReady) -> bool { ready.pid != 0 }

Try / catch

let ready = read_service_startup(&mut child, &launch, &control_path).await?;
if !valid_pid(&ready) {
    let _ = child.kill().await;
    return Err(anyhow!("detached FUSE service returned pid 0; service build is broken"));
}

Prevention

When it happens

Trigger: The service serializes ServiceStartup::Ready with an uninitialized or placeholder pid — e.g. it reports the pid of a parent it intends to exit, fails std::process::id(), or the struct field defaulted to 0 during construction.

Common situations: Custom daemonization code (double-fork) losing track of the real child pid; a service build where pid was never assigned before replying; running under a sandbox/runtime where process introspection returns 0.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-storage-provider-fuse/src/main.rs:716

    .await
    .context("timed out waiting for the FUSE service")??;
    if read == 0 {
        bail!("detached FUSE service exited before readiness");
    }
    if line.len() > 64 * 1024 {
        bail!("detached FUSE service exceeded the startup response size");
    }
    match serde_json::from_str(&line)? {
        ServiceStartup::Ready {
            mount_id,
            pid,
            access,
        } => {
            if !control_path.exists() {
                bail!("detached FUSE service did not retain its control endpoint");
            }
            if pid == 0 {
                bail!("detached FUSE service returned an invalid process identity");
            }
            Ok(ControlReady {
                mount_id,
                pid,
                access,
            })
        },
        ServiceStartup::Error { message } => bail!("detached FUSE service failed: {message}"),
    }
}

#[derive(Debug)]
struct ControlReady {
    mount_id: StorageMountId,
    pid: u32,
    access: StorageProviderAccessV1,
}

View on GitHub (pinned to affd8760f4)