rayon-rs/rayon · critical
The global thread pool has not been initialized.
Error message
The global thread pool has not been initialized.
What it means
Rayon operations that need the global thread pool (e.g. `current_num_threads`, `in_worker` contexts) panic when no global pool has been initialized and registry construction failed or was never triggered. The registry is created lazily via `call_once`; if it's absent/unavailable, this expect fires.
Solutions
- Check the Result of `ThreadPoolBuilder::new().num_threads(n).build_global()` at startup and log the error
- Pass a positive thread count (>= 1) to the builder
- Raise the container's process/thread limits (ulimit -u, cgroup pids.max)
- Fall back to sequential iterators (`for` loops) on platforms without thread support
Example fix
// before
rayon::spawn(task); // may panic if registry init failed earlier
// after
rayon::ThreadPoolBuilder::new().num_threads(4).build_global()
.expect("failed to init rayon global pool");
rayon::spawn(task); Defensive patterns
Strategy: validation
Validate before calling
fn init_rayon(threads: usize) -> Result<(), rayon::ThreadPoolBuildError> {
rayon::ThreadPoolBuilder::new().num_threads(threads.max(1)).build_global()
} Try / catch
match rayon::ThreadPoolBuilder::new().build_global() {
Ok(()) => {},
Err(e) => log::error!("rayon init failed: {e}"),
} Prevention
- Always check build_global() result at startup
- Use thread count >= 1
- Raise pid/thread limits in containers
- Provide sequential fallback on restricted platforms
When it happens
Trigger: Calling parallel code from a context where the global registry can't be built — notably inside a process where thread spawning fails, or when a previous `ThreadPoolBuilder::build_global()` error left THE_REGISTRY as None (e.g. unusable thread count = 0).
Common situations: Configuring `num_threads(0)` or hitting OS thread-creation limits in containers with low pid limits; running rayon in restricted environments (some WASM/embedded targets) where threads can't spawn.
Related errors
- FIFO is empty
- range start should be <= length
- range end should be <= length
- range start should be <= range end
- owner thread
AI-assisted analysis of rayon-rs/rayon@ee0a00bdb1 (2026-09-07).
Data as JSON: /api/errors/afcdbfa300676130.
Report an issue: GitHub.
Appendix: source
Thrown at rayon-core/src/registry.rs:169
// ////////////////////////////////////////////////////////////////////////
// Initialization
static mut THE_REGISTRY: Option<Arc<Registry>> = None;
static THE_REGISTRY_SET: Once = Once::new();
/// Starts the worker threads (if that has not already happened). If
/// initialization has not already occurred, use the default
/// configuration.
pub(super) fn global_registry() -> &'static Arc<Registry> {
set_global_registry(default_global_registry)
.or_else(|err| {
// SAFETY: we only create a shared reference to `THE_REGISTRY` after the `call_once`
// that initializes it, and there will be no more mutable accesses at all.
debug_assert!(THE_REGISTRY_SET.is_completed());
let the_registry = unsafe { &*ptr::addr_of!(THE_REGISTRY) };
the_registry.as_ref().ok_or(err)
})
.expect("The global thread pool has not been initialized.")
}
/// Starts the worker threads (if that has not already happened) with
/// the given builder.
pub(super) fn init_global_registry<S>(
builder: ThreadPoolBuilder<S>,
) -> Result<&'static Arc<Registry>, ThreadPoolBuildError>
where
S: ThreadSpawn,
{
set_global_registry(|| Registry::new(builder))
}
/// Starts the worker threads (if that has not already happened)
/// by creating a registry with the given callback.
fn set_global_registry<F>(registry: F) -> Result<&'static Arc<Registry>, ThreadPoolBuildError>
where
F: FnOnce() -> Result<Arc<Registry>, ThreadPoolBuildError>,View on GitHub (pinned to ee0a00bdb1)