GraphiteEditor/Graphite · error

Failed to spawn socket thread

Error message

Failed to spawn socket thread

What it means

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.

Source

Thrown at desktop/src/socket.rs:48

pub(crate) struct SocketHandle {
	thread: Option<thread::JoinHandle<()>>,
	shutdown_sender: mpsc::Sender<()>,
}
impl Drop for SocketHandle {
	fn drop(&mut self) {
		let _ = self.shutdown_sender.send(());
		let _ = self.thread.take().expect("SocketHandle can only be dropped once").join();
	}
}

pub(crate) fn start(app_event_scheduler: AppEventScheduler) -> SocketHandle {
	let (shutdown_sender, shutdown_receiver) = mpsc::channel();

	let thread = thread::Builder::new()
		.name("socket".to_string())
		.spawn(move || run(app_event_scheduler, shutdown_receiver))
		.expect("Failed to spawn socket thread");

	SocketHandle {
		shutdown_sender,
		thread: Some(thread),
	}
}

fn run(app_event_scheduler: AppEventScheduler, shutdown_receiver: mpsc::Receiver<()>) {
	let listener = match ListenerOptions::new()
		.name(socket_name())
		.nonblocking(ListenerNonblockingMode::Accept)
		.try_overwrite(true)
		.max_spin_time(Duration::from_millis(100))
		.create_sync()
	{
		Ok(listener) => listener,
		Err(error) => {
			tracing::error!("Failed to bind socket: {}", error);

View on GitHub (pinned to c507b35645)

Solutions

  1. Check the limit: ulimit -u and cat /sys/fs/cgroup/pids.max (or pids.current), then raise it (docker --pids-limit, systemd TasksMax)
  2. Free system memory or reduce concurrently running processes/threads
  3. Re-run the app when the machine is less loaded
  4. If it recurs, profile for thread leaks in the app (each socket start spawns exactly one thread, so repeated starts may leak SocketHandles)

Example fix

// before
let thread = thread::Builder::new()
	.name("socket".to_string())
	.spawn(move || run(app_event_scheduler, shutdown_receiver))
	.expect("Failed to spawn socket thread");

// after
let thread = thread::Builder::new()
	.name("socket".to_string())
	.spawn(move || run(app_event_scheduler, shutdown_receiver))
	.unwrap_or_else(|e| panic!("Failed to spawn socket thread: {e} (check ulimit -u / pids limit)"));
Defensive patterns

Strategy: retry

Validate before calling

// Before starting the socket, confirm the process can still create threads
fn can_spawn_thread() -> bool {
	std::thread::Builder::new().spawn(|| {}).map(|h| h.join().is_ok()).is_ok()
}

Try / catch

let mut thread = None;
for attempt in 0..3 {
	match thread::Builder::new().name("socket".to_string()).spawn(/* ... */) {
		Ok(t) => { thread = Some(t); break; }
		Err(e) if attempt < 2 => std::thread::sleep(Duration::from_millis(100)),
		Err(e) => panic!("Failed to spawn socket thread after retries: {e}"),
	}
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/b932bce5485392b9. Report an issue: GitHub.