astrid-runtime/astrid · error
an Astrid daemon appears to be running but its uplink is unr
Error message
an Astrid daemon appears to be running but its uplink is unreachable (missing or unlinked system.sock while the PID/lock is live). run `astrid restart`
What it means
`astrid status` treats a live recorded PID/lock with a missing or unlinked system.sock as a daemon whose uplink is gone. Because the process is still alive but cannot be contacted, status cannot be answered, and `start` refuses to force-recycle (it must not kill a booting daemon or an innocent recycled PID). The CLI bails with an actionable message directing the user to `astrid restart`, which performs an identity-gated SIGTERM→SIGKILL and respawns cleanly.
Source
Thrown at crates/astrid-cli/src/commands/daemon.rs:581
},
}
}
/// Handle `astrid status`.
pub(crate) async fn handle_status(output_format: OutputFormat) -> Result<()> {
validate_runtime_admission()?;
let socket_path = socket_client::proxy_socket_path();
let endpoint_present = astrid_core::local_transport::endpoint_is_present(&socket_path)
.context("failed to inspect daemon endpoint")
.unwrap_or(false);
let recorded_alive = recorded_daemon_pid_is_alive();
match decide_status_action(endpoint_present, recorded_alive) {
StatusAction::NotRunning => {
print_status(output_format, None)?;
return Ok(());
},
StatusAction::RunningButUnreachable => {
anyhow::bail!(
"an Astrid daemon appears to be running but its uplink is unreachable (missing or unlinked system.sock while the PID/lock is live). run `astrid restart`"
);
},
StatusAction::QueryLiveSocket => {},
}
let connect = tokio::time::timeout(
STATUS_CONNECT_TIMEOUT,
socket_client::connect_kernel_for_workspace(None),
)
.await;
let Ok(Ok(mut client)) = connect else {
if recorded_alive {
anyhow::bail!(
"an Astrid daemon appears to be running but its uplink is unreachable; run `astrid restart`"
);
}
print_status(output_format, None)?;View on GitHub (pinned to affd8760f4)
Solutions
- Run `astrid restart` — it identity-gates the recorded PID, SIGTERM/SIGKILLs it, cleans stale markers, and spawns a fresh daemon.
- Inspect the run dir (socket_path/pid_path) to confirm system.sock is actually gone and whether the recorded PID belongs to astrid (ps -p <pid>).
- If the PID was recycled by an unrelated process, remove the stale PID/lock files manually before starting.
- Prevent socket loss by pointing the runtime dir at a location not swept by tmp cleaners.
Example fix
// before (stale/unlinked socket with live PID) $ astrid status Error: an Astrid daemon appears to be running but its uplink is unreachable ... run `astrid restart` // after $ astrid restart $ astrid status # now reports the running daemon
Defensive patterns
Strategy: validation
Validate before calling
// before scripting around status, check reachability
let endpoint_present = astrid_core::local_transport::endpoint_is_present(&socket_client::proxy_socket_path())?;
let pid_alive = /* read pid file and check /proc */;
if pid_alive && !endpoint_present {
// skip status; go straight to `astrid restart`
run(vec!["astrid", "restart"])?;
} Type guard
fn daemon_is_reachable(endpoint_present: bool, recorded_alive: bool) -> bool {
// only trust status when the socket exists, not merely when a PID is live
endpoint_present && recorded_alive
} Try / catch
match astrid_status() {
Ok(status) => use(status),
Err(e) if e.to_string().contains("uplink is unreachable") => run(vec!["astrid", "restart"])?,
Err(e) => return Err(e),
} Prevention
- Point the runtime dir at a path not swept by tmp cleaners (avoid /tmp for system.sock).
- Prefer `astrid restart` over kill -9 so markers are cleaned and identity-checked.
- Monitor for the 'RunningButUnreachable' state rather than repeatedly polling status.
- Pin AX_HOME consistently in shell profiles/scripts so CLI and daemon share one run-dir.
When it happens
Trigger: Running `astrid status` when decide_status_action returns RunningButUnreachable: the PID file/lock records a live daemon but endpoint_is_present(proxy_socket_path()) is false — the system.sock was deleted, never created, or unlinked (e.g. tmp cleaner removed it, daemon half-crashed, or stale run-dir).
Common situations: Systemd-tmpfiles or periodic /tmp cleanup deleting the socket; daemon process wedged after SIGSTOP or deadlock so it stopped serving the socket; a crashed daemon whose PID file survived; running the CLI in a different container/mount namespace than the daemon so the socket path differs.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- daemon rejected capsule metadata request: {message}
- daemon rejected capsule metadata request: {message}
- Daemon returned an unexpected response to GetCommands
- an Astrid daemon appears to be running but its uplink is unr
- unexpected daemon response: {other:?}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/0805faa76278438b.
Report an issue: GitHub.