cube-js/cube · error

timed out after {}s waiting for {what} (last seen: {label}).

Error message

timed out after {}s waiting for {what} (last seen: {label}). {on_timeout}. Last error: {err}

What it means

`poll` waits for an asynchronous condition (e.g. deployment readiness) by repeatedly running `attempt()` until a terminal value or the deadline. When the deadline passes without a terminal value, it raises this timeout error. The message includes elapsed seconds, what was waited for, the last observed state label, streak info about recurring transient failures, an on-timeout hint, and the last error seen.

Source

Thrown at rust/cube-cli/src/wait.rs:110

    let Wait {
        what,
        timeout,
        interval,
        on_timeout,
    } = wait;
    let started = Instant::now();
    let mut last_label: Option<String> = None;
    // The CURRENT failure streak: how many consecutive transient failures, and the
    // most recent one's message. One variable rather than two, because the count and
    // the message are only ever meaningful together — and keeping a message past the
    // streak that produced it is how a blip recovered from at minute 1 ends up blamed
    // for a timeout at minute 30. `None` between streaks says exactly that.
    let mut streak: Option<(u32, String)> = None;

    // Same message whether the deadline lands between attempts or inside one.
    let timed_out =
        |elapsed: Duration, last_label: &Option<String>, streak: &Option<(u32, String)>| {
            anyhow::anyhow!(
                "timed out after {}s waiting for {what}{}{}{}",
                elapsed.as_secs(),
                match last_label {
                    Some(label) => format!(" (last seen: {label})"),
                    None => String::new(),
                },
                if on_timeout.is_empty() {
                    String::new()
                } else {
                    format!(". {on_timeout}")
                },
                match streak {
                    Some((_, err)) => format!(". Last error: {err}"),
                    None => String::new(),
                }
            )
        };

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Read the message: `last seen` and `Last error` identify what the wait was actually observing.
  2. Increase the timeout if the target is known to be slow (deadline may land inside an attempt; late answers are rejected).
  3. Fix the underlying condition the last error points at (e.g. crash-looping container, wrong health endpoint).
  4. Check network/firewall if attempts error transiently forever (the streak counter flags recurring failures).
Defensive patterns

Strategy: retry

Try / catch

match poll(what, attempt, timeout).await {
    Err(e) if e.to_string().starts_with("timed out after") => {
        // parse "last seen"/"Last error" from the message, decide to retry with longer timeout
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any `poll` call where `attempt()` never returns a terminal value before the computed deadline — e.g. a service never becomes healthy, or attempts keep erroring until `tokio::time::timeout_at(deadline, ...)` expires. Raised in tests `a_never_answering_attempt_still_times_out`, `an_answer_after_the_deadline_is_not_accepted`, and others.

Common situations: Waiting for a deployment/pod that is crash-looping, a service that starts slower than the configured timeout, or a perpetually failing check (bad endpoint) whose transient errors keep repeating (streak noted in message).

Understand the failure class

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/972fc1c269cfdc59. Report an issue: GitHub.