astrid-runtime/astrid · error

job exceeded the s wall-clock ceiling

Error message

job exceeded the {timeout_secs}s wall-clock ceiling

What it means

Raised in run_job_under when the job's response stream does not complete before the user-configured wall-clock timeout. Before returning the error, the code sends a cooperative cancel so the daemon aborts the in-flight turn promptly (delete remains the hard stop).

Solutions

  1. Increase the --timeout value to cover the expected job duration
  2. Check the daemon logs to see whether the job was hung vs genuinely slow
  3. Split the job into smaller steps; cancel/delete the agent if it is wedged

Example fix

// before
astrid-agent spawn --job big-refactor --timeout 30
// after
astrid-agent spawn --job big-refactor --timeout 600
Defensive patterns

Strategy: retry

Validate before calling

// estimate expected duration and pick a timeout with headroom
let timeout_secs = std::cmp::max(estimated_secs * 3, 300);

Try / catch

match run(...).await { Err(e) if e.to_string().contains("wall-clock ceiling") => { eprintln!("timed out; retrying with a larger --timeout"); run_with_timeout(timeout_secs * 4).await }, other => other }

Prevention

When it happens

Trigger: The submitted job takes longer than timeout_secs to produce its final marker: a very long agent turn, a hung upstream model call, or a timeout set too low for the workload.

Common situations: Running long analysis jobs with the default short timeout, a daemon stalled on a slow upstream API, or a typo'd job that blocks waiting for input.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

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

    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;
            Err(anyhow!(
                "job exceeded the {timeout_secs}s wall-clock ceiling"
            ))
        },
    };

    // Best-effort disconnect; the connection also closes on drop.
    let disconnect = astrid_types::ipc::IpcMessage::new(
        astrid_types::Topic::client_disconnect(),
        astrid_types::ipc::IpcPayload::Disconnect {
            reason: Some("spawn".to_string()),
        },
        session.0,
    );
    let _ = client.send_message(disconnect).await;

    result
}

View on GitHub (pinned to affd8760f4)