astrid-runtime/astrid · error

failed to connect as '{principal}': {e}

Error message

failed to connect as '{principal}': {e}

What it means

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.

Source

Thrown at crates/astrid-cli/src/commands/agent/spawn.rs:162

            eprintln!("[spawn] job failed: {job:#}");
            eprintln!("[spawn] teardown also failed: {teardown:#}");
            Ok(ExitCode::from(1))
        },
    }
}

/// Connect an uplink authenticated AS the throwaway, submit the job, and drain
/// the response under a wall-clock ceiling. On timeout, send the cooperative
/// cancel sentinel; the hard guarantee is the caller's teardown regardless.
async fn run_job_under(
    principal: &PrincipalId,
    session: &SessionId,
    job: &str,
    timeout_secs: u64,
) -> Result<String> {
    let mut client = socket_client::connect_for_workspace(session.clone(), principal.clone(), None)
        .await
        .map_err(|e| anyhow!("failed to connect as '{principal}': {e}"))?;

    client
        .send_input(job.to_string())
        .await
        .context("failed to submit job")?;

    let drained = tokio::time::timeout(
        Duration::from_secs(timeout_secs),
        drain_until_final(&mut client, session),
    )
    .await;

    let result = match drained {
        Ok(inner) => inner,
        Err(_elapsed) => {
            // Cooperative cancel so the react capsule aborts the in-flight turn
            // promptly; delete (which reclaims) is the hard stop regardless.
            let _ = send_cancel(&mut client, session).await;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Start/restart the daemon and confirm the socket file exists
  2. Verify the principal is authorized and has filesystem permission on the socket
  3. Check the daemon logs for the root connect error in the {e} suffix

Example fix

// before
astrid-agent spawn --job run-tests   # daemon not running
// after
astrid-agent daemon start && astrid-agent spawn --job run-tests
Defensive patterns

Strategy: retry

Validate before calling

if !std::path::Path::new(&socket_path).exists() { eprintln!("daemon socket not found; start the daemon first"); std::process::exit(1); }

Try / catch

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) }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/de89b3765cf1086b. Report an issue: GitHub.