sinelaw/fresh · critical

TypeScript plugin thread creation failed

Error message

TypeScript plugin thread creation failed: {}

What it means

The plugin manager's `new` constructor spawns a dedicated thread for the TypeScript plugin runtime. If thread creation fails it logs the error via tracing, and — only in debug builds (`#[cfg(debug_assertions)]`) — panics with "TypeScript plugin thread creation failed". In release builds the panic is compiled out and the editor continues with plugins unavailable; in debug builds it aborts editor startup.

Solutions

  1. Check the underlying error `e` in the log line for the OS reason (e.g. 'Os { code: 11, ... }' = EAGAIN).
  2. Raise the thread/process limit: ulimit -u, container pids cgroup limit, or systemd TasksMax.
  3. Free threads by reducing concurrently spawned plugin threads, or restart the environment if the limit was leaked.
  4. As a workaround for development sessions, launch with --no-plugins to skip spawning the TypeScript plugin thread.

Example fix

// before (debug build, thread limit exhausted)
panic!("TypeScript plugin thread creation failed: {}", e);

// after: raise the limit instead of patching code
# ulimit -u 4096   # or raise container pids.max / systemd TasksMax
Defensive patterns

Strategy: fallback

Validate before calling

// Rust: check the process/thread budget before spawning (Linux)
let limits = std::fs::read_to_string("/proc/sys/kernel/threads-max")?;
let used: i32 = std::fs::read_to_string("/proc/self/status")?
    .lines().find(|l| l.starts_with("Threads:"))
    .and_then(|l| l.split_whitespace().nth(1))
    .unwrap_or("0").parse()?;
let max: i32 = limits.trim().parse()?;
let can_spawn = used < max - 16;

Try / catch

// the spawn returns Result; handle it without panicking even in debug
if let Err(e) = thread::Builder::new().name("ts-plugins".into()).spawn(...) {
    tracing::error!("TypeScript plugin thread creation failed: {}", e);
    // continue with plugins disabled
}

Prevention

When it happens

Trigger: Constructing PluginManager::new with plugins enabled (no --no-plugins flag) when std::thread::Builder::spawn returns Err — practically only when the OS refuses to create a thread (thread/resource limits hit, e.g. RLIMIT_NPROC/ulimit -u exhausted, out-of-memory in debug builds, cgroup thread caps).

Common situations: Development/debug builds running under low ulimit -u, containers with low pids.max cgroup limits, or a heavily loaded system that already exhausted the process/thread budget. Never occurs in release builds from this code path.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/a38381535bd88823. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/services/plugins/manager.rs:86

                let services = Arc::new(EditorServiceBridge {
                    command_registry: command_registry.clone(),
                    dir_context,
                    theme_cache,
                    local_plugin_fs,
                    window_registry: Arc::clone(&window_registry),
                });
                match PluginThreadHandle::spawn(services) {
                    Ok(handle) => {
                        return Self {
                            inner: Some(handle),
                            window_registry: Some(window_registry),
                            pending_injected_commands: Vec::new(),
                        }
                    }
                    Err(e) => {
                        tracing::error!("Failed to spawn TypeScript plugin thread: {}", e);
                        #[cfg(debug_assertions)]
                        panic!("TypeScript plugin thread creation failed: {}", e);
                    }
                }
            } else {
                tracing::info!("Plugins disabled via --no-plugins flag");
            }
            Self {
                inner: None,
                window_registry: None,
                pending_injected_commands: Vec::new(),
            }
        }

        #[cfg(not(feature = "plugins"))]
        {
            let _ = command_registry; // Suppress unused warning
            let _ = dir_context; // Suppress unused warning
            let _ = theme_cache; // Suppress unused warning
            let _ = authority_filesystem; // Suppress unused warning

View on GitHub (pinned to 67894ca546)