tinyhumansai/openhuman · error · anyhow::Error

[claude_agent_sdk] subprocess timed out while reading output

Error message

[claude_agent_sdk] subprocess timed out while reading output

What it means

The claude_agent_sdk provider spawns the `claude` CLI as a child process and reads NDJSON messages from its stdout under a 120-second tokio timeout (subprocess.rs:158). This error fires when that outer timeout elapses before stdout reaches EOF — i.e. the CLI produced no complete output stream within 2 minutes. The child is explicitly killed before bailing, so no orphan process remains.

Source

Thrown at src/openhuman/inference/provider/claude_agent_sdk/subprocess.rs:214

                    }
                    Err(e) => {
                        tracing::warn!(
                            error = %e,
                            line_len = line.len(),
                            "[claude_agent_sdk] failed to parse ndjson line"
                        );
                    }
                }
            }
            anyhow::Ok(())
        })
        .await;

        match read_result {
            Ok(inner) => inner?,
            Err(_) => {
                let _ = child.kill().await;
                anyhow::bail!("[claude_agent_sdk] subprocess timed out while reading output");
            }
        }

        let status = timeout(Duration::from_secs(30), child.wait())
            .await
            .map_err(|_| {
                anyhow::anyhow!("[claude_agent_sdk] subprocess timed out while waiting for exit")
            })??;
        let stderr_output = stderr_task.await.unwrap_or_default();
        tracing::debug!("[claude_agent_sdk] subprocess exited status={}", status);

        if let Some(err) = error_message {
            anyhow::bail!("[claude_agent_sdk] error from claude CLI: {err}");
        }

        // Use the final result message if present; otherwise join streaming text parts.
        let output = result_text
            .filter(|s| !s.is_empty())

View on GitHub (pinned to 7491200858)

Solutions

  1. Retry the request — transient CLI/network stalls are the most common cause and a fresh turn often completes.
  2. Check the core debug log for `[claude_agent_sdk]` lines: if the last NDJSON line arrived recently, the turn was genuinely long; consider raising the 120s timeout in subprocess.rs:158 to fit your workload.
  3. Run `claude --version` and update the CLI; older builds hang on permission/trust prompts in non-interactive mode.
  4. Audit configured MCP servers (`claude mcp list`) for a server that never completes its handshake and stalls the turn.

Example fix

// before
let read_result = timeout(Duration::from_secs(120), async { /* read stdout */ }).await;

// after — size the deadline to the workload (long agentic turns)
let read_result = timeout(Duration::from_secs(600), async { /* read stdout */ }).await;
Defensive patterns

Strategy: retry

Validate before calling

// Rust — pre-flight the CLI before routing long turns to it
async fn claude_sdk_ready() -> bool {
    tokio::process::Command::new("claude")
        .arg("--version")
        .output()
        .await
        .map(|o| o.status.success())
        .unwrap_or(false)
}

Try / catch

match provider.chat(req).await {
    Err(e) if e.to_string().contains("timed out while reading output") => {
        // transient stall: retry once with backoff, then surface to user
        tokio::time::sleep(Duration::from_secs(3)).await;
        provider.chat(req).await
    }
    other => other,
}

Prevention

When it happens

Trigger: A `claude` SDK-mode request where the CLI hangs: a very long-running agentic turn exceeding 120s, a CLI waiting on a stuck MCP server or permission prompt, a deadlocked stdin pipe, or a network stall between the CLI and Anthropic APIs. Triggered by the `Err(_elapsed)` arm of `timeout(Duration::from_secs(120), ...)` around the stdout line-reading loop.

Common situations: Long agent turns (tool-heavy sessions routinely exceed 2 minutes), corporate proxies that stall the CLI's HTTPS connection, a claude CLI version that blocks on interactive trust prompts when run non-interactively, or an MCP server configured in the project that never responds.

Understand the failure class

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/1b577b441cfa186c. Report an issue: GitHub.