zeroclaw-labs/zeroclaw · error

Final summary LLM call timed out after {step_secs}s (step_ti

Error message

Final summary LLM call timed out after {step_secs}s (step_timeout_secs)

What it means

On the graceful max-iteration exit, ZeroClaw pops in a tools-free 'summarize your work' prompt and sends one final LLM call wrapped in tokio::time::timeout(pacing.step_timeout_secs). If that call exceeds the step timeout, the summary prompt is popped back off the history and the turn bails with this error — so a slow final summary turns an otherwise complete run into a failure.

Source

Thrown at crates/zeroclaw-runtime/src/agent/turn/max_iter.rs:150

                    tokio::select! {
                        () = token.cancelled() => SummaryCall::Cancelled,
                        result = summary_future => SummaryCall::Done(result),
                    }
                } else {
                    SummaryCall::Done(summary_future.await)
                }
            }
        }
    };

    let resp = match summary_call {
        SummaryCall::Cancelled => {
            history.pop();
            return Err(ToolLoopCancelled.into());
        }
        SummaryCall::TimedOut(step_secs) => {
            history.pop();
            anyhow::bail!("Final summary LLM call timed out after {step_secs}s (step_timeout_secs)")
        }
        SummaryCall::Done(Err(e)) => {
            ::zeroclaw_log::record!(
                ERROR,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_category(::zeroclaw_log::EventCategory::Provider)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({
                        "model": model,
                        "provider": provider_name,
                        "max_iterations": max_iterations,
                        "trace_id": turn_id,
                        "error": format!("{e}"),
                    })),
                "final summary LLM call failed after iteration exhaustion; bailing"
            );
            history.pop();
            return Err(e).context(format!(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Raise pacing.step_timeout_secs so the final summary fits comfortably (it is one full-context completion).
  2. Reduce context before the cap is hit (prune/compact history) so the summary call is cheaper.
  3. Switch to a faster model for summary-grade output, or lower max_iterations so the summary happens earlier.
  4. If the partial transcript is enough, catch this error and use the accumulated display text already streamed for the turn.

Example fix

# before
# pacing.step_timeout_secs = 30 — final summary needs longer on a big context

# after
# pacing.step_timeout_secs = 120
Defensive patterns

Strategy: retry

Try / catch

Catch the timeout error and retry the turn's summary once with a raised step_timeout_secs (or a faster model); if retrying is unacceptable, degrade gracefully by returning the accumulated display text already streamed for the turn — the summary prompt is popped from history, so the transcript stays clean.

Prevention

When it happens

Trigger: pacing.step_timeout_secs set low relative to the model's latency; a long accumulated history making the summary call slow; provider congestion right at the end of an iteration-capped turn.

Common situations: Tight step timeout tuned for quick tool steps but too small for one big final completion; reasoning models with long time-to-first-token; large context after many tool rounds.

Understand the failure class

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/40584676ff9449a5. Report an issue: GitHub.