xai-org/grok-build · error · io::Error

blocking pool pre-warm stalled after {started} of {n} thread

Error message

blocking pool pre-warm stalled after {started} of {n} threads

What it means

The blocking-pool pre-warm waits for a bounded time for each requested worker thread to signal readiness. If a worker parks without confirming within the timeout, the function releases the workers already started and returns io::ErrorKind::TimedOut with how many of the requested threads actually started.

Source

Thrown at crates/codegen/xai-tty-utils/src/runtime.rs:140

    drop(ready_tx);

    let deadline = Instant::now().checked_add(wait);
    let mut workers = Vec::with_capacity(n);
    for started in 0..n {
        let got = match deadline {
            Some(d) => ready_rx
                .recv_timeout(d.saturating_duration_since(Instant::now()))
                .ok(),
            None => ready_rx.recv().ok(),
        };
        match got {
            Some(thread) => workers.push(thread),
            None => {
                release_parked_workers(release, &workers);
                while let Ok(thread) = ready_rx.try_recv() {
                    thread.unpark();
                }
                return Err(io::Error::new(
                    io::ErrorKind::TimedOut,
                    format!("blocking pool pre-warm stalled after {started} of {n} threads"),
                ));
            }
        }
    }
    Ok(workers)
}

fn release_parked_workers(release: &AtomicBool, workers: &[std::thread::Thread]) {
    release.store(true, Ordering::Release);
    for thread in workers {
        thread.unpark();
    }
}

#[cfg(test)]
#[path = "runtime_tests.rs"]

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Raise the container/host process/thread limits (cgroup pids.max, ulimit -u) and retry
  2. Reduce the requested pre-warm thread count to match the environment's capacity
  3. Check for a deadlock in the worker park path — if started is always 0, the ready signaling is broken
Defensive patterns

Strategy: try-catch

Try / catch

match prewarm_blocking_pool_n(n) {
    Err(e) if e.kind() == io::ErrorKind::TimedOut => {
        log::warn!("pre-warm incomplete: {e}; continuing with available workers");
        // degraded mode: proceed or retry with a smaller n
    }
    Err(e) => return Err(e.into()),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: System under heavy CPU load or thread-creation limits (ulimit -u, cgroup pids.max, RLIMIT_NPROC) preventing new threads from starting or being scheduled within the pre-warm window; a misbehaving park path that never signals the ready channel.

Common situations: Containerized deployments with a low pids limit; extremely loaded CI machines; forking (run_behavior_child) right after pre-warm when resources are constrained.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/6454b997ff3cfb77. Report an issue: GitHub.