astrid-runtime/astrid · error
Daemon rejected connection
Error message
Daemon rejected connection: {reason} What it means
perform_handshake_in_home performs the client handshake against the daemon's socket in the home directory and expects an OK response carrying optional auth state. If the daemon replies with a non-ok status, the library bails with the daemon-supplied reason (or 'unknown error' when absent), so the caller learns why authentication/handshake was refused.
Solutions
- Read the reason string in the error and fix the stated cause (e.g. re-authenticate or regenerate client credentials).
- Re-run the login/enrollment flow so the client's home-directory credentials match what the daemon expects.
- Confirm the daemon version and handshake protocol match the client crate; upgrade whichever is stale.
- Check daemon-side limits (max connections, auth mode) that could make it reject new handshakes.
Example fix
// before: stale credentials in home cause rejection let client = SocketClient::connect_default().await?; // Daemon rejected connection: invalid token // after: refresh credentials before connecting auth::login(&config).await?; // refreshes the home credential file let client = SocketClient::connect_default().await?;
Defensive patterns
Strategy: validation
Validate before calling
// Verify credentials exist and look well-formed before attempting a handshake
fn creds_look_valid(home: &Path) -> bool {
let cred = home.join(".astrid/credentials");
cred.exists() && std::fs::read(&cred).map(|b| !b.is_empty()).unwrap_or(false)
}
if !creds_look_valid(&dirs::home_dir().unwrap()) {
auth::login(&config).await?; // refresh before connecting
} Try / catch
match perform_handshake(&client).await {
Ok(auth) => auth,
Err(e) if e.to_string().contains("Daemon rejected connection") => {
let reason = e.to_string();
if reason.contains("token") || reason.contains("auth") {
auth::login(&config).await?; // re-authenticate and retry once
perform_handshake(&client).await?
} else { return Err(e); }
}
Err(e) => return Err(e),
} Prevention
- Refresh credentials proactively before they are rotated out on the daemon.
- Match client and daemon versions so handshake protocols agree.
- Surface the daemon's reason string in your own logs for fast triage.
- Verify you are connecting as the user whose home holds valid credentials.
When it happens
Trigger: Calling perform_handshake (directly or via clients like the test helpers) when the daemon answers the handshake request with an error status — bad or missing credentials/token, handshake protocol version mismatch, daemon not accepting new connections, or device/session not registered.
Common situations: Client credentials are stale or rotated on the daemon side; daemon requires re-authentication after key rotation; a different user's home directory lacks the expected credentials file; daemon policy (capacity/auth mode) changed between versions.
Related errors
- Admin request timed out after
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon appears to be running but its uplink is…
- an Astrid daemon is recorded as running (PID file) but its…
- anyhow::anyhow!(error)
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/e68226b10b16bc5d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-uplink/src/socket_client.rs:569
);
let signature = keypair.sign(message.as_bytes()).to_hex();
let signed = HandshakeRequest {
token: token.to_hex(),
protocol_version: PROTOCOL_VERSION,
client_version: env!("CARGO_PKG_VERSION").to_string(),
claimed_principal: Some(principal.to_string()),
signature: Some(signature),
};
(send_request_read_response(stream, &signed).await?, true)
} else {
(response, false)
};
if !response.is_ok() {
let reason = response
.reason
.unwrap_or_else(|| "unknown error".to_string());
anyhow::bail!("Daemon rejected connection: {reason}");
}
Ok(authenticated)
}
/// Test-only access to the production handshake framing with an explicit home.
#[cfg(feature = "test-support")]
#[doc(hidden)]
pub async fn perform_handshake_for_test(
stream: &mut LocalStream,
principal: &PrincipalId,
home: &astrid_core::dirs::AstridHome,
) -> Result<bool> {
perform_handshake_in_home(stream, principal, home).await
}
/// Write one length-prefixed [`HandshakeRequest`] frame and read the
/// length-prefixed [`HandshakeResponse`] frame, with per-operation timeouts.View on GitHub (pinned to affd8760f4)