{"record":{"id":"b932bce5485392b9","repo":"GraphiteEditor/Graphite","slug":"failed-to-spawn-socket-thread","errorCode":null,"errorMessage":"Failed to spawn socket thread","messagePattern":"Failed to spawn socket thread","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"desktop/src/socket.rs","lineNumber":48,"sourceCode":"\npub(crate) struct SocketHandle {\n\tthread: Option<thread::JoinHandle<()>>,\n\tshutdown_sender: mpsc::Sender<()>,\n}\nimpl Drop for SocketHandle {\n\tfn drop(&mut self) {\n\t\tlet _ = self.shutdown_sender.send(());\n\t\tlet _ = self.thread.take().expect(\"SocketHandle can only be dropped once\").join();\n\t}\n}\n\npub(crate) fn start(app_event_scheduler: AppEventScheduler) -> SocketHandle {\n\tlet (shutdown_sender, shutdown_receiver) = mpsc::channel();\n\n\tlet thread = thread::Builder::new()\n\t\t.name(\"socket\".to_string())\n\t\t.spawn(move || run(app_event_scheduler, shutdown_receiver))\n\t\t.expect(\"Failed to spawn socket thread\");\n\n\tSocketHandle {\n\t\tshutdown_sender,\n\t\tthread: Some(thread),\n\t}\n}\n\nfn run(app_event_scheduler: AppEventScheduler, shutdown_receiver: mpsc::Receiver<()>) {\n\tlet listener = match ListenerOptions::new()\n\t\t.name(socket_name())\n\t\t.nonblocking(ListenerNonblockingMode::Accept)\n\t\t.try_overwrite(true)\n\t\t.max_spin_time(Duration::from_millis(100))\n\t\t.create_sync()\n\t{\n\t\tOk(listener) => listener,\n\t\tErr(error) => {\n\t\t\ttracing::error!(\"Failed to bind socket: {}\", error);","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/desktop/src/socket.rs#L30-L66","documentation":"Panic when thread::Builder::spawn fails for the named 'socket' thread that runs the interprocess local socket server (used by editor frontends/tools to talk to a running Graphite instance). spawn returns an io::Error when the OS refuses to create the thread: the process hit its thread/process limit (RLIMIT_NPROC, cgroup pids.max), or memory for the thread stack could not be allocated.","triggerScenarios":"Launching Graphite inside a container or service with a low pids cgroup limit or ulimit -u while many threads/processes are already running; heavy parallel cargo builds sharing the same machine limits; severe memory pressure preventing stack allocation.","commonSituations":"Docker/Kubernetes pods with pids.max set low; Linux user namespaces with restrictive RLIMIT_NPROC; machines under heavy load during CI runs of the desktop app.","solutions":["Check the limit: ulimit -u and cat /sys/fs/cgroup/pids.max (or pids.current), then raise it (docker --pids-limit, systemd TasksMax)","Free system memory or reduce concurrently running processes/threads","Re-run the app when the machine is less loaded","If it recurs, profile for thread leaks in the app (each socket start spawns exactly one thread, so repeated starts may leak SocketHandles)"],"exampleFix":"// before\nlet thread = thread::Builder::new()\n\t.name(\"socket\".to_string())\n\t.spawn(move || run(app_event_scheduler, shutdown_receiver))\n\t.expect(\"Failed to spawn socket thread\");\n\n// after\nlet thread = thread::Builder::new()\n\t.name(\"socket\".to_string())\n\t.spawn(move || run(app_event_scheduler, shutdown_receiver))\n\t.unwrap_or_else(|e| panic!(\"Failed to spawn socket thread: {e} (check ulimit -u / pids limit)\"));","handlingStrategy":"retry","validationCode":"// Before starting the socket, confirm the process can still create threads\nfn can_spawn_thread() -> bool {\n\tstd::thread::Builder::new().spawn(|| {}).map(|h| h.join().is_ok()).is_ok()\n}","typeGuard":null,"tryCatchPattern":"let mut thread = None;\nfor attempt in 0..3 {\n\tmatch thread::Builder::new().name(\"socket\".to_string()).spawn(/* ... */) {\n\t\tOk(t) => { thread = Some(t); break; }\n\t\tErr(e) if attempt < 2 => std::thread::sleep(Duration::from_millis(100)),\n\t\tErr(e) => panic!(\"Failed to spawn socket thread after retries: {e}\"),\n\t}\n}","preventionTips":["Raise ulimit -u / cgroup pids limits in environments that run the desktop app","Keep exactly one SocketHandle per process and drop it on shutdown to avoid thread leaks","Distinguish transient EAGAIN thread-spawn failures from hard errors and retry briefly"],"tags":["rust","threading","os-limits","ipc","socket"],"backgroundTag":"thread-creation-failed","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}