astrid-runtime/astrid · error · anyhow::Error
inspect detached FUSE service after stderr sink handoff
Error message
inspect detached FUSE service after stderr sink handoff
What it means
After the stderr sink handoff, require_service_running_after_handoff polls the detached FUSE child with try_wait. If the child has already exited, it bails with the exit status, kills/waits as needed, and wraps any try_wait error with this context plus the final status.
Solutions
- Fix the detached service so it stays running after readiness — check the reported exit status for the root cause
- Validate FUSE mount options and mountpoint permissions before spawning the detached service
- Ensure nothing else reaps the child PID (double wait) so try_wait does not error
- Add a startup health/liveness check so a fast-exiting service is caught before handoff
Defensive patterns
Strategy: retry
Validate before calling
// verify the service stays up for a settle window after readiness
for _ in 0..10 {
if Path::new(&pid_file).exists() { break; }
tokio::time::sleep(Duration::from_millis(100)).await;
}
assert!(Path::new(&pid_file).exists(), "FUSE service exited right after readiness"); Try / catch
match result {
Err(err) if err.to_string().contains("inspect detached FUSE service after stderr sink handoff") => {
eprintln!("detached FUSE service exited/unstable after readiness: {err:#}");
// retry spawn with backoff, then give up
}
other => other?,
} Prevention
- Validate FUSE mount options and permissions before detaching the service
- Wait for a liveness confirmation (pid file/mount visible) before considering startup done
- Avoid double-reaping the child so try_wait is not called on an invalid handle
When it happens
Trigger: try_wait returns Err(error) while verifying the detached FUSE service is still alive after readiness — the child handle is invalid or the OS reports a wait error; Ok(Some(status)) takes the separate bail! path with the exit status.
Common situations: The FUSE service exits immediately after reporting readiness (bad mount options, permission denied on the mountpoint); the child process handle became invalid; PID reaping races in the supervisor.
Related errors
- read detached FUSE stderr during sink handoff
- detached FUSE service did not retain its control endpoint
- detached FUSE service exceeded the startup response size
- detached FUSE service failed
- detached FUSE service returned an invalid process identity
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/861381946709e61a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-fuse/src/main.rs:668
if stderr.is_empty() {
Err(error.context(status_context))
} else {
Err(error.context(format!("{status_context}; stderr: {stderr}")))
}
},
}
}
async fn require_service_running_after_handoff(
child: &mut tokio::process::Child,
) -> Result<()> {
match child.try_wait() {
Ok(None) => Ok(()),
Ok(Some(status)) => bail!("detached FUSE service exited after readiness: {status}"),
Err(error) => {
let _ = child.kill().await;
let status = child.wait().await;
Err(anyhow::Error::new(error)
.context("inspect detached FUSE service after stderr sink handoff")
.context(format!("detached FUSE service status: {status:?}")))
},
}
}
async fn read_service_startup(
child: &mut tokio::process::Child,
launch: &ServiceLaunch,
control_path: &Path,
) -> Result<ControlReady> {
let mut stdin = child
.stdin
.take()
.context("FUSE service stdin is unavailable")?;
let bytes = serde_json::to_vec(launch)?;
stdin.write_all(&bytes).await?;
stdin.write_all(b"\n").await?;View on GitHub (pinned to affd8760f4)