{"record":{"id":"c51461f1c2aadb34","repo":"astrid-runtime/astrid","slug":"an-astrid-daemon-appears-to-be-running-but-its-upl-c51461","errorCode":null,"errorMessage":"an Astrid daemon appears to be running but its uplink is unreachable; run `astrid restart`","messagePattern":"an Astrid daemon appears to be running but its uplink is unreachable; run `astrid restart`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/daemon.rs","lineNumber":595,"sourceCode":"            print_status(output_format, None)?;\n            return Ok(());\n        },\n        StatusAction::RunningButUnreachable => {\n            anyhow::bail!(\n                \"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`\"\n            );\n        },\n        StatusAction::QueryLiveSocket => {},\n    }\n\n    let connect = tokio::time::timeout(\n        STATUS_CONNECT_TIMEOUT,\n        socket_client::connect_kernel_for_workspace(None),\n    )\n    .await;\n    let Ok(Ok(mut client)) = connect else {\n        if recorded_alive {\n            anyhow::bail!(\n                \"an Astrid daemon appears to be running but its uplink is unreachable; run `astrid restart`\"\n            );\n        }\n        print_status(output_format, None)?;\n        return Ok(());\n    };\n    let status = status_response(\n        client\n            .request(KernelRequest::GetStatus)\n            .await\n            .context(\"Failed to query daemon status\")?,\n    )?;\n    print_status(output_format, Some(&status))?;\n    Ok(())\n}\n\nfn print_status(output_format: OutputFormat, status: Option<&DaemonStatus>) -> Result<()> {\n    if output_format == OutputFormat::Json {","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/daemon.rs#L577-L613","documentation":"After deciding the daemon might be live, `astrid status` attempts a socket connection to the kernel under STATUS_CONNECT_TIMEOUT. If connecting or the timeout fails while a recorded daemon PID is still alive, the CLI bails: the daemon process exists but its uplink (the Unix socket) is not serviceable. Like the pre-connect variant, the fix is `astrid restart`, since start will not force-recycle a live PID.","triggerScenarios":"`astrid status` where socket exists enough to attempt a connect, but connect_kernel_for_workspace(None) errors or exceeds STATUS_CONNECT_TIMEOUT, and recorded_daemon_pid_is_alive() is true.","commonSituations":"Daemon hung (event loop blocked) so it accepts nothing; socket file present but listener closed; permission mismatch on the socket file; boot-time race where the daemon hasn't bound the socket yet within the timeout; SELinux/AppArmor blocking local socket connects.","solutions":["Run `astrid restart` to identity-gatedly kill and respawn the daemon.","Re-run `astrid status` once after a short wait — a booting daemon may just need to finish binding the socket.","Check socket file permissions/ownership in the Astrid runtime dir.","If it recurs, inspect daemon logs for a wedged event loop or listener crash."],"exampleFix":"// before\n$ astrid status\nError: an Astrid daemon appears to be running but its uplink is unreachable; run `astrid restart`\n// after\n$ astrid restart && astrid status","handlingStrategy":"retry","validationCode":"// pre-check: is the socket connectable before invoking status?\nlet connect = tokio::time::timeout(STATUS_CONNECT_TIMEOUT, socket_client::connect_kernel_for_workspace(None)).await;\nif connect.is_err() && recorded_daemon_pid_is_alive() {\n    run(vec![\"astrid\", \"restart\"])?; // heal before asking for status\n}","typeGuard":"fn is_unreachable_uplink(err: &anyhow::Error) -> bool {\n    err.to_string().contains(\"uplink is unreachable\")\n}","tryCatchPattern":"match astrid_status().await {\n    Ok(s) => s,\n    Err(e) if is_unreachable_uplink(&e) => {\n        run(vec![\"astrid\", \"restart\"]).await?;\n        astrid_status().await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Allow STATUS_CONNECT_TIMEOUT headroom on slow/loaded machines so a booting daemon isn't misread as unreachable.","After CLI upgrades, always restart the daemon to avoid protocol/behavior drift.","Check socket file permissions and LSM (SELinux/AppArmor) denials when this recurs.","Use `astrid status` after `restart` to confirm the new daemon bound its socket."],"tags":["daemon","socket","timeout","ipc"],"backgroundTag":"connection-refused","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}