denoland/deno · critical
failed to spawn desktop runtime thread
Error message
failed to spawn desktop runtime thread
What it means
The desktop runtime library (cli/rt_desktop) runs the entire Deno event loop on a dedicated thread with an 8 MiB stack, matching the stack headroom the OS gives the CLI main thread - smaller stacks overflow on deep module graphs. If the OS itself refuses to create that thread, .expect() panics with 'failed to spawn desktop runtime thread' and the process aborts.
Source
Thrown at cli/rt_desktop/lib.rs:1451
/// Stack size for the thread the Deno runtime runs on. Laufey invokes
/// `laufey_runtime_start` on its RuntimeLoader thread, which gets the
/// platform-default stack for secondary threads — only 512KB on macOS.
/// That is far too small for the runtime: synchronous module parsing (swc)
/// alone recurses past it on real-world module graphs (e.g. a SvelteKit
/// dev server's graph) and kills the process with SIGBUS. 8MB matches the
/// headroom the CLI gets from the OS main thread.
const RUNTIME_THREAD_STACK_SIZE: usize = 8 * 1024 * 1024;
/// Run `f` to completion on a dedicated thread with a stack large enough
/// for the Deno runtime (see `RUNTIME_THREAD_STACK_SIZE`), keeping the
/// calling (loader) thread parked until it finishes.
fn run_on_runtime_thread<F: FnOnce() + Send + 'static>(f: F) {
let thread = std::thread::Builder::new()
.name("deno-desktop-runtime".to_string())
.stack_size(RUNTIME_THREAD_STACK_SIZE)
.spawn(f)
.expect("failed to spawn desktop runtime thread");
if thread.join().is_err() {
// The runtime thread panicked. The panic hook normally exits the
// process itself; this is a backstop in case it returned.
deno_runtime::exit(1);
}
}
/// Run as a headless worker (no Laufey window). Used when a framework dev
/// server forks child processes that re-execute this dylib.
fn run_headless_worker() {
denort::init_logging(None, None);
deno_runtime::deno_permissions::mark_standalone();
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.unwrap();
let rt = deno_runtime::tokio_util::create_basic_runtime();
View on GitHub (pinned to f7822238ca)
Solutions
- Raise thread/process limits: ulimit -u, cgroup pids.max, systemd TasksMax
- Reduce the number of concurrently spawned child processes and workers
- Free memory or raise the container memory limit so the 8 MiB stack can be reserved
- If it reproduces with plain `deno run`, file a deno issue with environment details
Defensive patterns
Strategy: validation
Validate before calling
# preflight before launching the desktop runtime ulimit -u ps -eLf | wc -l cat /sys/fs/cgroup/pids.max 2>/dev/null || cat /sys/fs/cgroup/pids/pids.max 2>/dev/null # if the process count is near the limit, raise the limit or reduce children first
Prevention
- Set adequate ulimit -u / cgroup pids.max / systemd TasksMax in deployment units
- Bound the number of concurrent child processes and worker threads your app spawns
- Monitor thread counts and memory so exhaustion is caught before the panic
When it happens
Trigger: Starting an application that loads the desktop runtime dylib when thread/process creation fails: RLIMIT_NPROC (ulimit -u) or cgroup pids.max exhausted, insufficient memory to reserve the 8 MiB stack, or a sandbox that forbids thread creation.
Common situations: CI containers and orchestrators with low pids limits; framework dev servers that fork many child processes; heavily parallel test machines; memory-constrained devices or containers.
Related errors
- REPL thread panicked
- desktop mode enabled
- pledge test permissions called before restoring previous ple
- restore test permissions token does not match the stored tok
- pledge test permissions called before restoring previous ple
AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20).
Data as JSON: /api/errors/aea1b7d7ae7fa00a.
Report an issue: GitHub.