{"record":{"id":"e68226b10b16bc5d","repo":"astrid-runtime/astrid","slug":"daemon-rejected-connection-reason","errorCode":null,"errorMessage":"Daemon rejected connection: {reason}","messagePattern":"Daemon rejected connection: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-uplink/src/socket_client.rs","lineNumber":569,"sourceCode":"        );\n        let signature = keypair.sign(message.as_bytes()).to_hex();\n        let signed = HandshakeRequest {\n            token: token.to_hex(),\n            protocol_version: PROTOCOL_VERSION,\n            client_version: env!(\"CARGO_PKG_VERSION\").to_string(),\n            claimed_principal: Some(principal.to_string()),\n            signature: Some(signature),\n        };\n        (send_request_read_response(stream, &signed).await?, true)\n    } else {\n        (response, false)\n    };\n\n    if !response.is_ok() {\n        let reason = response\n            .reason\n            .unwrap_or_else(|| \"unknown error\".to_string());\n        anyhow::bail!(\"Daemon rejected connection: {reason}\");\n    }\n\n    Ok(authenticated)\n}\n\n/// Test-only access to the production handshake framing with an explicit home.\n#[cfg(feature = \"test-support\")]\n#[doc(hidden)]\npub async fn perform_handshake_for_test(\n    stream: &mut LocalStream,\n    principal: &PrincipalId,\n    home: &astrid_core::dirs::AstridHome,\n) -> Result<bool> {\n    perform_handshake_in_home(stream, principal, home).await\n}\n\n/// Write one length-prefixed [`HandshakeRequest`] frame and read the\n/// length-prefixed [`HandshakeResponse`] frame, with per-operation timeouts.","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-uplink/src/socket_client.rs#L551-L587","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: stale credentials in home cause rejection\nlet client = SocketClient::connect_default().await?; // Daemon rejected connection: invalid token\n// after: refresh credentials before connecting\nauth::login(&config).await?; // refreshes the home credential file\nlet client = SocketClient::connect_default().await?;","handlingStrategy":"validation","validationCode":"// Verify credentials exist and look well-formed before attempting a handshake\nfn creds_look_valid(home: &Path) -> bool {\n    let cred = home.join(\".astrid/credentials\");\n    cred.exists() && std::fs::read(&cred).map(|b| !b.is_empty()).unwrap_or(false)\n}\nif !creds_look_valid(&dirs::home_dir().unwrap()) {\n    auth::login(&config).await?; // refresh before connecting\n}","typeGuard":null,"tryCatchPattern":"match perform_handshake(&client).await {\n    Ok(auth) => auth,\n    Err(e) if e.to_string().contains(\"Daemon rejected connection\") => {\n        let reason = e.to_string();\n        if reason.contains(\"token\") || reason.contains(\"auth\") {\n            auth::login(&config).await?; // re-authenticate and retry once\n            perform_handshake(&client).await?\n        } else { return Err(e); }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["handshake","authentication","ipc","daemon"],"backgroundTag":"authentication-required","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}