tinyhumansai/openhuman · error · anyhow::Error
[claude-code][driver] turn timed out after {:?}
Error message
[claude-code][driver] turn timed out after {:?} What it means
The entire claude-code turn — spawn, stream, and exit — is wrapped in a hard 300-second (`TURN_TIMEOUT`, driver.rs:19) tokio timeout. When the child does not finish within 5 minutes, the driver logs a timeout, explicitly kills the child (kill_on_drop is the backstop; the explicit kill lets stderr be collected), and bails with this message.
Source
Thrown at src/openhuman/inference/provider/claude_code/driver.rs:476
let status = child
.wait()
.await
.map_err(|e| anyhow::anyhow!("wait child: {e}"))?;
Ok::<_, anyhow::Error>(status)
})
.await;
let status = match timed {
Ok(inner) => inner?,
Err(_elapsed) => {
log::error!(
"[claude-code][driver] turn timeout ({TURN_TIMEOUT:?}) exceeded; killing child"
);
// kill_on_drop handles cleanup, but explicit kill gives us
// a chance to collect stderr.
let _ = child.kill().await;
anyhow::bail!(
"[claude-code][driver] turn timed out after {:?}",
TURN_TIMEOUT
);
}
};
let stderr_text = stderr_task.await.unwrap_or_default();
if !status.success() {
anyhow::bail!(
"[claude-code][driver] exit {:?} stderr={}",
status.code(),
stderr_text.trim()
);
}
if let Some(err) = mapper.error.clone() {
anyhow::bail!("[claude-code][driver] {}", err);
}View on GitHub (pinned to 7491200858)
Solutions
- Retry once — transient stalls (proxy hiccup, API slowdown) commonly clear.
- If turns legitimately exceed 5 minutes in your workload, raise `TURN_TIMEOUT` in driver.rs:19 (it is a single const) and rebuild.
- Check `claude mcp list` for MCP servers whose tools hang; fix or remove them.
- Ensure the project dir is pre-trusted (run `claude` once interactively in it) so the CLI never blocks on a trust prompt.
Example fix
// before const TURN_TIMEOUT: Duration = Duration::from_secs(300); // after — allow long agentic turns const TURN_TIMEOUT: Duration = Duration::from_secs(900);
Defensive patterns
Strategy: retry
Try / catch
let attempt = tokio::time::timeout(Duration::from_secs(330), provider.chat(req)).await;
match attempt {
Ok(Err(e)) if e.to_string().contains("turn timed out") => {
// one retry; a second consecutive timeout indicates a hung MCP tool or stall
provider.chat(req).await
}
other => other.unwrap_or_else(|_| Err(anyhow::anyhow!("outer watchdog fired"))),
} Prevention
- Pre-trust project directories (one interactive `claude` run) so the CLI never blocks on prompts.
- Keep MCP tool calls time-bounded so they can't consume the whole 300s turn budget.
- Match TURN_TIMEOUT to your real workload; long refactors need a larger const.
When it happens
Trigger: Any claude-code turn whose CLI process runs longer than 300s: a large refactor with many tool iterations, a hung MCP tool call, a CLI permission prompt blocking in non-interactive mode, or a stalled network uplink. Fires from the `Err(_elapsed)` arm of `tokio::time::timeout(TURN_TIMEOUT, ...)` at driver.rs:434.
Common situations: Big agentic coding tasks that legitimately exceed 5 minutes; an MCP server tool that never returns; the CLI waiting for interactive trust approval on a new project directory; saturated corporate proxy blocking streaming responses.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- [claude_agent_sdk] subprocess timed out while reading output
- [claude-code][driver] {}
- oauthAuthReadinessUserMessage(quick.reason)
- Model testing is only available in the desktop app.
- Model test RPC returned no result for ${workload} via ${prov
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/dfcdb837a20e84ec.
Report an issue: GitHub.