{"record":{"id":"6059fefd741f2a83","repo":"espanso/espanso","slug":"unable-to-spawn-worker-monitor-thread","errorCode":null,"errorMessage":"Unable to spawn worker monitor thread","messagePattern":"Unable to spawn worker monitor thread","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"espanso/src/cli/daemon/mod.rs","lineNumber":325,"sourceCode":"    std::thread::Builder::new()\n        .name(\"worker-status-monitor\".to_string())\n        .spawn(move || {\n            let result = child.wait();\n            if let Ok(status) = result {\n                if let Some(code) = status.code() {\n                    if code != WORKER_SUCCESS {\n                        exit_notify\n                            .send(code)\n                            .expect(\"unable to forward worker exit code\");\n                    }\n                } else {\n                    exit_notify\n                        .send(WORKER_ERROR_EXIT_NO_CODE)\n                        .expect(\"unable to forward worker exit code\");\n                }\n            }\n        })\n        .expect(\"Unable to spawn worker monitor thread\");\n}\n\nfn restart_worker(\n    paths: &Paths,\n    paths_overrides: &PathsOverrides,\n    exit_notify: Sender<i32>,\n    start_reason: Option<String>,\n) {\n    match create_ipc_client_to_worker(&paths.runtime) {\n        Ok(mut worker_ipc) => {\n            if let Err(err) = worker_ipc.send_async(IPCEvent::Exit) {\n                error!(\"unable to send termination signal to worker process: {err}\");\n            }\n        }\n        Err(err) => {\n            error!(\"could not establish IPC connection with worker: {err}\");\n        }\n    }","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/espanso/espanso/blob/e6c3736675048026a057f1c4803d59242482fa7b/espanso/src/cli/daemon/mod.rs#L307-L343","documentation":"After spawning the worker child, spawn_worker starts a 'worker-status-monitor' thread via std::thread::Builder::new().spawn to watch the child and forward its exit code. If the thread cannot be created, the expect panics with this message, leaving the worker unmonitored.","triggerScenarios":"std::thread::Builder::spawn returns Err, i.e. the OS refuses to allocate a new thread — thread-count limits (RLIMIT_NPROC / cgroup pids), out of memory for the new stack, or a security policy blocking thread creation.","commonSituations":"Systems at their max thread/process limit, containers with low pids.cgroup limits, severe memory exhaustion, hardened sandboxes restricting clone().","solutions":["Check and raise thread/process limits (ulimit -u, container pids limit)","Free memory or reduce concurrent threads in the environment","Confirm the process is not leaking threads that exhausted the limit","Convert the expect into error handling that retries thread creation or exits gracefully"],"exampleFix":"// before\n.expect(\"Unable to spawn worker monitor thread\");\n// after\nif let Err(err) = std::thread::Builder::new()\n    .name(\"worker-status-monitor\".to_string())\n    .spawn(move || { /* monitor loop */ })\n{\n    error!(\"Unable to spawn worker monitor thread: {err}\");\n}","handlingStrategy":"try-catch","validationCode":"","typeGuard":null,"tryCatchPattern":"match std::thread::Builder::new().name(\"worker-status-monitor\".to_string()).spawn(move || { /* ... */ }) {\n    Ok(handle) => { /* keep handle */ },\n    Err(err) => { error!(\"monitor thread spawn failed: {err}\"); }\n}","preventionTips":["Avoid exhausting thread limits (check pids cgroup, ulimit -u)","Monitor memory pressure; thread stacks need allocation","Reduce long-lived threads in the host process"],"tags":["thread","process","resource-limit"],"backgroundTag":"thread-spawn-failed","analyzedSha":"e6c3736675048026a057f1c4803d59242482fa7b","analyzedAt":"2026-09-06T15:16:34.240Z","contentChangedAt":"2026-09-06T15:16:34.240Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}