leptos-rs/leptos · critical

Executor::spawn_local called, but no global 'spawn_local' fu

Error message

Executor::spawn_local called, but no global 'spawn_local' function is configured.

What it means

This panic is the release-mode (or non-tracing) counterpart for spawn_local: any_spawner has no global 'spawn_local' implementation registered, so the no_op_spawn_local placeholder is invoked and panics. It means local (same-thread) task spawning was requested before an executor was configured.

Source

Thrown at any_spawner/src/lib.rs:88

         without threading?)."
    );
}

// Wasm panics if you spawn without an executor
#[cfg(feature = "wasm-bindgen")]
#[cold]
#[inline(never)]
fn no_op_spawn(_: PinnedFuture<()>) {
    panic!(
        "Executor::spawn called, but no global 'spawn' function is configured."
    );
}

#[cfg(not(debug_assertions))]
#[cold]
#[inline(never)]
fn no_op_spawn_local(_: PinnedLocalFuture<()>) {
    panic!(
        "Executor::spawn_local called, but no global 'spawn_local' function \
         is configured."
    );
}

/// Errors that can occur when using the executor.
#[derive(Error, Debug)]
pub enum ExecutorError {
    /// The executor has already been set.
    #[error("Global executor has already been set.")]
    AlreadySet,
}

/// A global async executor that can spawn tasks.
pub struct Executor;

impl Executor {
    /// Spawns a thread-safe [`Future`].

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Register a global local executor via any_spawner::spawn_local::set_executor(...) (e.g. LocalSet on native, wasm-bindgen on wasm) at startup, before spawning.
  2. In Leptos, verify the platform init ran (client hydration or server bootstrap) which wires up the executor.
  3. Confirm you call spawn_local only from the thread that owns the registered LocalSet/executor.

Example fix

// before
let rt = tokio::runtime::Runtime::new().unwrap();
Executor::spawn_local(async { do_work().await }); // panics: no local executor
// after
let local = tokio::task::LocalSet::new();
any_spawner::spawn_local::set_executor(any_spawner::LocalExecutor::TokioLocalSet(local.clone()));
local.spawn_local(async { do_work().await });
Defensive patterns

Strategy: validation

Validate before calling

// idempotent local-executor installation before any spawn_local
fn ensure_local_executor() {
    thread_local! { static INIT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) }; }
    INIT.with(|i| {
        if !i.get() {
            // install appropriate LocalSet/wasm executor here
            i.set(true);
        }
    });
}

Prevention

When it happens

Trigger: Calling Executor::spawn_local in a build without a configured global spawn_local function (no any_spawner::spawn_local::set_executor call), e.g. on native or in release builds where the debug handler is compiled out.

Common situations: Using spawn_local for DOM-touching tasks in wasm without executor init; native apps spawning thread-local futures without a LocalSet-based executor registered; debug vs release differences hiding the richer debug message.

Related errors


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