astrid-runtime/astrid · error
daemon closed guard uplink
Error message
daemon closed guard uplink
What it means
hold_guard_uplink keeps the MCP session guard's uplink alive by reading raw frames from the daemon client in a loop. When read_raw_frame returns Ok(None) the daemon has closed the connection (EOF), and the guard bails with this error because an active guard cannot persist without the daemon uplink. It exists to surface daemon-side disconnects rather than silently ending the guard session.
Source
Thrown at crates/astrid-cli/src/commands/mcp/session_guard.rs:54
principal: &astrid_core::PrincipalId,
daemon_root: &Path,
) -> Result<std::convert::Infallible> {
daemon::ensure_daemon_quiet("mcp-session-guard", Some(daemon_root)).await?;
let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());
let c =
crate::socket_client::connect_for_workspace(session, principal.clone(), Some(daemon_root))
.await
.context("failed to connect guard uplink to daemon")?;
validate_guard_auth(principal, c.is_authenticated())?;
debug!(%principal, "MCP session guard: daemon uplink established");
let mut client = c;
loop {
match client.read_raw_frame().await {
Ok(Some(_)) => {},
Ok(None) => anyhow::bail!("daemon closed guard uplink"),
Err(e) => return Err(e).context("guard uplink read failed"),
}
}
}
fn validate_guard_auth(principal: &astrid_core::PrincipalId, authenticated: bool) -> Result<()> {
if authenticated || *principal == astrid_core::PrincipalId::anonymous() {
return Ok(());
}
anyhow::bail!(
"guard uplink authenticated as anonymous instead of requested principal '{principal}'"
)
}
#[cfg(test)]
mod tests {
use super::*;View on GitHub (pinned to affd8760f4)
Solutions
- Check daemon logs for why the guard uplink was closed (shutdown, revocation, crash) at the matching time.
- Restart the daemon and re-establish the session guard.
- Make the guard resilient: on Ok(None), reconnect to the daemon and re-run the guard handshake instead of bailing.
- Run the daemon under a supervisor (systemd) so it restarts and the guard can re-acquire the uplink.
Example fix
// before
Ok(None) => anyhow::bail!("daemon closed guard uplink"),
// after
Ok(None) => {
// reconnect with backoff instead of failing the guard session
client = reconnect_guard_uplink(principal).await?;
continue;
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify daemon liveness before holding the guard uplink
if !is_daemon_alive().await {
anyhow::bail!("daemon not running; guard uplink will close immediately");
} Try / catch
match hold_guard_uplink(client, principal).await {
Ok(()) => {},
Err(e) if e.to_string().contains("daemon closed guard uplink") => {
// daemon went away: restart/reconnect and re-establish the guard
restart_daemon_and_reacquire_guard(principal).await?;
},
Err(e) => return Err(e),
} Prevention
- Run the daemon under a supervisor (systemd/supervisord) so uplinks survive crashes and upgrades.
- Avoid restarting the daemon while session guards are active; drain guards first.
- Distinguish clean EOF (Ok(None)) from read errors in logs to tell intentional close from failure.
- Implement reconnect-with-backoff in the guard loop rather than treating any disconnect as fatal.
When it happens
Trigger: The daemon process shuts down or crashes while the session guard is holding the uplink; the daemon explicitly closes the guard connection (e.g. session revoked, shutdown of the guard service); network/socket drop producing a clean EOF (Ok(None)) rather than an Err.
Common situations: Daemon restarted for an upgrade while a guard was active; system shutdown or OOM kill of the daemon; daemon closing the guard because the session expired or was revoked; socket cleanup by an init/supervisor.
Related errors
- daemon connection closed before command result
- guard uplink authenticated as anonymous instead of requested
- Daemon closed the connection before responding
- daemon returned an unexpected status response
- daemon request: {e}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/badc786633e79f68.
Report an issue: GitHub.