leptos-rs/leptos · critical

could not create futures executor ThreadPool

Error message

could not create futures executor ThreadPool

What it means

This panic comes from lazily initializing the global futures-executor ThreadPool inside `init_futures_executor` (any_spawner). `futures::executor::ThreadPool::new()` returns a Result and fails only when the underlying thread pool builder cannot spawn any worker threads (typically when `ThreadPoolBuilder::build()` hits an OS-level thread creation failure). any_spawner wraps this in `.expect`, so pool creation failure aborts the process on the first spawned task.

Source

Thrown at any_spawner/src/lib.rs:266

    pub fn init_futures_executor() -> Result<(), ExecutorError> {
        use futures::{
            executor::{LocalPool, LocalSpawner, ThreadPool},
            task::{LocalSpawnExt, SpawnExt},
        };
        use std::cell::RefCell;

        // Keep the lazy-init ThreadPool and thread-local LocalPool for spawn_local impl
        static THREAD_POOL: OnceLock<ThreadPool> = OnceLock::new();
        thread_local! {
            static LOCAL_POOL: RefCell<LocalPool> = RefCell::new(LocalPool::new());
            // SPAWNER is derived from LOCAL_POOL, keep it for efficiency inside the closure
            static SPAWNER: LocalSpawner = LOCAL_POOL.with(|pool| pool.borrow().spawner());
        }

        fn get_thread_pool() -> &'static ThreadPool {
            THREAD_POOL.get_or_init(|| {
                ThreadPool::new()
                    .expect("could not create futures executor ThreadPool")
            })
        }

        let executor_impl = ExecutorFns {
            spawn: |fut| {
                get_thread_pool()
                    .spawn(fut)
                    .expect("failed to spawn future on ThreadPool");
            },
            spawn_local: |fut| {
                // Use the thread_local SPAWNER derived from LOCAL_POOL
                SPAWNER.with(|spawner| {
                    spawner
                        .spawn_local(fut)
                        .expect("failed to spawn local future");
                });
            },
            poll_local: || {

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Raise the thread/process limit (ulimit -u, container pids cgroup) and free memory, then restart the application
  2. Ensure exactly one executor init and that another executor (tokio, async-executor, glib) is available; call `init_tokio`/`init_async_executor` instead of `init_futures_executor` on thread-restricted platforms
  3. Wrap pool creation context: run the app in an environment where `std::thread::Builder::new().spawn()` succeeds (verify with a small probe before bootstrapping)
  4. Pin/upgrade any_spawner and futures-executor versions so ThreadPoolBuilder configuration matches your runtime

Example fix

// before
any_spawner::executor::init_futures_executor().expect("init executor");
// after
#[cfg(feature = "tokio")]
any_spawner::executor::init_tokio().expect("init tokio executor");
#[cfg(not(feature = "tokio"))]
any_spawner::executor::init_futures_executor().expect("init futures executor");
Defensive patterns

Strategy: validation

Validate before calling

fn can_spawn_thread() -> bool {
    std::thread::Builder::new()
        .name("probe".into())
        .spawn(|| {})
        .map(|h| { let _ = h.join(); true })
        .unwrap_or(false)
}
assert!(can_spawn_thread(), "thread creation unavailable; raise ulimit/pids limit or use another executor");

Try / catch

// init itself returns Result; only pool creation inside panics
if let Err(any_spawner::ExecutorError::AlreadySet) = any_spawner::executor::init_futures_executor() {
    // already initialized elsewhere: fine
}
// guard the environment first (validationCode) since failure here panics

Prevention

When it happens

Trigger: Calling `any_spawner::_executor::init_futures_executor()` (directly or via Leptos' platform bootstrap) and then spawning the first future, on a system where thread creation fails: thread/process rlimit exhausted (RLIMIT_NPROC), memory exhaustion, restricted container/seccomp sandbox forbidding clone/pthread_create, or a heavily cgroup-limited environment.

Common situations: Running in minimal Docker containers or Kubernetes pods with low pids limits (e.g. pids.max), serverless/WASM-adjacent or CI sandboxes that disallow spawning threads, machines that have hit `ulimit -u`, or memory-starved hosts where the futures ThreadPool builder cannot allocate its threads.

Related errors


AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01). Data as JSON: /api/errors/4011e9d62523cdb6. Report an issue: GitHub.