{"record":{"id":"de89b3765cf1086b","repo":"astrid-runtime/astrid","slug":"failed-to-connect-as-principal-e","errorCode":null,"errorMessage":"failed to connect as '{principal}': {e}","messagePattern":"failed to connect as '(.+?)': (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/agent/spawn.rs","lineNumber":162,"sourceCode":"            eprintln!(\"[spawn] job failed: {job:#}\");\n            eprintln!(\"[spawn] teardown also failed: {teardown:#}\");\n            Ok(ExitCode::from(1))\n        },\n    }\n}\n\n/// Connect an uplink authenticated AS the throwaway, submit the job, and drain\n/// the response under a wall-clock ceiling. On timeout, send the cooperative\n/// cancel sentinel; the hard guarantee is the caller's teardown regardless.\nasync fn run_job_under(\n    principal: &PrincipalId,\n    session: &SessionId,\n    job: &str,\n    timeout_secs: u64,\n) -> Result<String> {\n    let mut client = socket_client::connect_for_workspace(session.clone(), principal.clone(), None)\n        .await\n        .map_err(|e| anyhow!(\"failed to connect as '{principal}': {e}\"))?;\n\n    client\n        .send_input(job.to_string())\n        .await\n        .context(\"failed to submit job\")?;\n\n    let drained = tokio::time::timeout(\n        Duration::from_secs(timeout_secs),\n        drain_until_final(&mut client, session),\n    )\n    .await;\n\n    let result = match drained {\n        Ok(inner) => inner,\n        Err(_elapsed) => {\n            // Cooperative cancel so the react capsule aborts the in-flight turn\n            // promptly; delete (which reclaims) is the hard stop regardless.\n            let _ = send_cancel(&mut client, session).await;","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/agent/spawn.rs#L144-L180","documentation":"Raised in run_job_under when socket_client::connect_for_workspace fails to establish a connection to the daemon socket as the given principal. The underlying connection error (socket missing, permission denied, handshake rejected) is wrapped with the principal name for context.","triggerScenarios":"Daemon not running or its socket file absent, socket path wrong for the workspace, the principal lacks permission to open the socket, or the daemon crashed mid-startup.","commonSituations":"Running the spawn command before starting the daemon, running as a different user than the daemon owner so the Unix socket is inaccessible, stale socket file after an unclean daemon shutdown.","solutions":["Start/restart the daemon and confirm the socket file exists","Verify the principal is authorized and has filesystem permission on the socket","Check the daemon logs for the root connect error in the {e} suffix"],"exampleFix":"// before\nastrid-agent spawn --job run-tests   # daemon not running\n// after\nastrid-agent daemon start && astrid-agent spawn --job run-tests","handlingStrategy":"retry","validationCode":"if !std::path::Path::new(&socket_path).exists() { eprintln!(\"daemon socket not found; start the daemon first\"); std::process::exit(1); }","typeGuard":null,"tryCatchPattern":"match run_job_under(...).await { Err(e) if e.to_string().contains(\"failed to connect\") => { start_daemon()?; retry_once(); }, Err(e) => return Err(e), Ok(r) => Ok(r) }","preventionTips":["Ensure the daemon is running before spawning jobs","Check socket file permissions match the invoking user","Health-check the daemon (ping/status) before submitting jobs"],"tags":["network","ipc","connection"],"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"}