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
- Raise pacing.step_timeout_secs so the final summary fits comfortably (it is one full-context completion).
- Reduce context before the cap is hit (prune/compact history) so the summary call is cheaper.
- Switch to a faster model for summary-grade output, or lower max_iterations so the summary happens earlier.
- 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
- Budget step_timeout_secs for one full-context completion, not just tool steps
- Prune/compact history before the iteration cap so the final summary is cheap
- Prefer prompt models for capped long-running turns when latency matters
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Schema missing required 'type' field
- ACP elicitation/create timed out after {timeout:?}
- ACP elicitation/create (multi) timed out after {timeout:?}
- ACP request_permission timed out after {:?}
- interaction followup token expired (id {interaction_id}, >15
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/40584676ff9449a5.
Report an issue: GitHub.