leptos-rs/leptos · critical
At {caller}, tried to spawn a Future with Executor::spawn_lo
Error message
At {caller}, tried to spawn a Future with Executor::spawn_local() before a global executor was initialized. What it means
Debug-build panic raised by handle_uninitialized_spawn_local when Executor::spawn_local is called before a global spawn_local executor is registered. It includes the {caller} location to identify the offending call site. It is the debug counterpart of the release-mode no_op_spawn_local panic.
Source
Thrown at any_spawner/src/lib.rs:504
/// Handles the case where `Executor::spawn_local` is called without an initialized executor.
#[cold] // Less likely path
#[inline(never)]
#[track_caller]
fn handle_uninitialized_spawn_local(_fut: PinnedLocalFuture<()>) {
let caller = std::panic::Location::caller();
#[cfg(all(debug_assertions, feature = "tracing"))]
{
tracing::error!(
target: "any_spawner",
spawn_caller=%caller,
"Executor::spawn_local called before a global executor was initialized. \
Task likely dropped or panicked."
);
// Fall through to panic or no-op depending on build/target
}
#[cfg(all(debug_assertions, not(feature = "tracing")))]
{
panic!(
"At {caller}, tried to spawn a Future with \
Executor::spawn_local() before a global executor was initialized."
);
}
// In release builds (without tracing), call the specific no-op function (which usually panics).
#[cfg(not(debug_assertions))]
{
no_op_spawn_local(_fut);
}
}
View on GitHub (pinned to 32d20f6c9d)
Solutions
- Register the local executor via any_spawner::spawn_local::set_executor(...) before the first spawn_local call.
- Ensure the registration happens on the same thread the futures are spawned on (LocalSet semantics).
- Use the {caller} in the message to locate the premature spawn_local and move it after initialization.
- If the message is replaced by a no-op panic in release, reproduce in debug to get the caller info, then fix init order.
Example fix
// before
fn handler() {
Executor::spawn_local(async { update_dom().await }); // panics in debug
}
// after
fn handler() {
ensure_executor_installed(); // any_spawner::spawn_local::set_executor(...)
Executor::spawn_local(async { update_dom().await });
} Defensive patterns
Strategy: try-catch
Validate before calling
fn spawn_local_task<F: Future<Output = ()> + 'static>(fut: F) {
crate::init::ensure_local_executor_installed();
any_spawner::spawn_local(fut);
} Try / catch
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
any_spawner::spawn_local(async {});
})).err().map(|p| eprintln!("spawn_local before executor init: {:?}", p)); Prevention
- Use the {caller} in debug panics to locate premature spawn_local calls.
- Keep executor installation on the same thread as spawn_local (LocalSet).
- Add an integration test that exercises spawn_local paths in debug mode.
When it happens
Trigger: Executor::spawn_local invoked with no configured global spawn_local function, in a debug build without the tracing feature.
Common situations: Tests or early startup code using spawn_local before executor setup; wasm code paths where only the non-local executor was registered; switching frameworks/feature flags and losing the LocalSet registration.
Related errors
- Executor::spawn_local called, but no global 'spawn_local' fu
- At {caller}, tried to spawn a Future with Executor::spawn()
- Executor::spawn called, but no global 'spawn' function is co
- failed to spawn local future
- called `unwrap_{variant}()` on a non-`{variant}` variant of
AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01).
Data as JSON: /api/errors/4a7ada64ad183835.
Report an issue: GitHub.