tinyhumansai/openhuman · error · SubmitError

pool worker closed stdout

Error message

pool worker closed stdout

What it means

Returned from the response-reading loop when the worker's stdout stream ends (responses.next_line() yields None) while waiting for a job completion line. It means the pool worker process exited or closed its stdout mid-job — e.g. a reused idle worker died between jobs, or the child crashed. Distinguished from the timeout path so that the fixed deadline is only spent on real waiting; lines that fail to parse or carry a mismatched id are skipped without resetting the deadline.

Source

Thrown at src/openhuman/runtime/pool/worker.rs:256

        // Fixed deadline: `continue`ing over unparseable / mismatched-id lines
        // must NOT reset the wedged-worker timeout, so it bounds the total wait.
        let deadline = hard_timeout.map(|t| tokio::time::Instant::now() + t);
        loop {
            let next = match deadline {
                Some(dl) => match tokio::time::timeout_at(dl, self.responses.next_line()).await {
                    Ok(inner) => inner,
                    Err(_) => {
                        return Err(SubmitError::post(anyhow::anyhow!(
                            "pool worker job timed out (hard deadline; worker wedged)"
                        )))
                    }
                },
                None => self.responses.next_line().await,
            };
            let line = match next {
                Ok(Some(line)) => line,
                Ok(None) => {
                    return Err(SubmitError::post(anyhow::anyhow!(
                        "pool worker closed stdout"
                    )))
                }
                Err(error) => {
                    return Err(SubmitError::post(
                        anyhow::Error::new(error).context("reading pool job response"),
                    ))
                }
            };
            let response: PoolJobResponse = match serde_json::from_str(&line) {
                Ok(response) => response,
                Err(error) => {
                    tracing::warn!(
                        lang = self.launch.lang.id(),
                        "[runtime_pool] unparseable worker line skipped: {error}"
                    );
                    continue;
                }

View on GitHub (pinned to 7491200858)

Solutions

  1. Treat as terminal: the job may or may not have run before the worker died, so do not auto-retry the same job id.
  2. Replace/restart the dead pool worker before submitting further jobs.
  3. Log the worker pid and job id to correlate with any partial side effects the job may have produced.
  4. Check stderr/exit status of the worker process for the underlying crash cause (OOM, panic in the spawned tool).
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/openhuman/runtime/pool/worker.rs:256 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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