ramensoftware/windhawk · critical

spawn the background startup thread

Error message

spawn the background startup thread

What it means

Same pattern as the event-pump spawn panic: .expect("spawn the background startup thread") panics when the OS refuses to create the background startup thread that runs workspace sweeping, the profile watcher, and pump::startup::kick. Unlike the pump thread this happens just before the app returns Ok(()), so the window opens but startup work (profile watcher, background catalog refresh) silently never runs if the spawn were allowed to fail — hence the panic.

Solutions

  1. Raise RLIMIT_NPROC / systemd TasksMax / cgroup pids.max for the process's user.
  2. Ensure enough free memory for a new thread stack (check dmesg/OOM logs, add swap).
  3. Audit and fix thread leaks so the process reaches startup with quota available.
  4. Consider converting this expect to a soft-degraded path if background work should be optional (upstream change).
Defensive patterns

Strategy: fallback

Validate before calling

fn can_spawn_thread() -> bool {
    std::thread::Builder::new().spawn(|| {}).map(|h| h.join().is_ok()).unwrap_or(false)
}

Try / catch

// check the Result before expecting:
if let Err(e) = std::thread::Builder::new().spawn(move || { /* startup work */ }) {
    log::warn!("background startup thread unavailable: {e}; continuing degraded");
}

Prevention

When it happens

Trigger: std::thread::Builder spawn returns Err at the end of the Tauri setup closure: RLIMIT_NPROC / cgroup pids limit reached, out-of-memory when allocating the thread stack, or thread creation refused near process shutdown.

Common situations: Containerized or restricted environments hitting pids/TasksMax caps; low-memory systems; earlier leaked threads in the same session consuming the quota before UI startup.

Related errors


AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12). Data as JSON: /api/errors/677ef7dadb1b6271. Report an issue: GitHub.

Appendix: source

Thrown at src/windhawk-core/ui/src/lib.rs:761

            let background_link = link.clone();
            // As with the pump: setup has nowhere to report a refused thread.
            #[allow(clippy::expect_used)]
            std::thread::Builder::new()
                .name("wh-ui-background".to_owned())
                .spawn(move || {
                    background_link.wait_until_settled();
                    // The install-tree ModsRuntime -> Engine\Mods copy of
                    // libc++/libunwind/the mod shim, for files not already present.
                    background_ctx.host.seed_mods_runtime();
                    // Garbage-collect abandoned editor workspaces. The manager's
                    // lock serializes it against any allocate a handler starts.
                    commands::dev::sweep_abandoned_workspaces(&background_ctx);
                    // The profile watcher (update-availability / ratings refresh)
                    // and the background catalog refresh.
                    pump::profile_watch::spawn(background_ctx.clone());
                    pump::startup::kick(&background_ctx);
                })
                .expect("spawn the background startup thread");

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            wh_ipc,
            wh_log_backlog,
            wh_log_stop_capture,
            broker::wh_broker_state,
            broker::wh_broker_retry,
            splash::wh_splash_ready,
            splash::wh_splash_presented
        ])
        .run(tauri::generate_context!())
        .expect("error while running the Windhawk UI");
}

/// The stored UI theme setting, read from `getAppSettings` once at startup to seed the
/// native shell before the window opens. A read failure is the dark default (as is any

View on GitHub (pinned to 61d99ed8e1)