GraphiteEditor/Graphite · error

failed to construct async-message tokio runtime

Error message

failed to construct async-message tokio runtime

What it means

Panic when tokio::runtime::Builder::new_multi_thread().worker_threads(1).enable_all().build() returns an Err inside TokioSpawner, the editor's async-message executor. The multi-thread runtime build fails when the OS cannot create the worker thread (thread limits, memory), or — more commonly on Linux — when enable_all's I/O driver cannot be set up because epoll_create1/timerfd_create fail (fd exhaustion or a seccomp sandbox blocking those syscalls).

Source

Thrown at editor/src/messages/future/future_message_handler.rs:175

}

#[cfg(not(target_family = "wasm"))]
impl Default for TokioSpawner {
	fn default() -> Self {
		Self { runtime: std::sync::OnceLock::new() }
	}
}

#[cfg(not(target_family = "wasm"))]
impl TokioSpawner {
	fn runtime(&self) -> &tokio::runtime::Runtime {
		self.runtime.get_or_init(|| {
			tokio::runtime::Builder::new_multi_thread()
				.worker_threads(1)
				.thread_name("graphite-async")
				.enable_all()
				.build()
				.expect("failed to construct async-message tokio runtime")
		})
	}
}

#[cfg(not(target_family = "wasm"))]
impl MessageSpawner for TokioSpawner {
	fn spawn(&self, future: InnerMessageFuture, results: UnboundedSender<Message>, wake: Wake) {
		self.runtime().spawn(async move {
			let message = future.await;
			let _ = results.unbounded_send(message);
			wake();
		});
	}
}

#[cfg(target_family = "wasm")]
struct WasmSpawner;

View on GitHub (pinned to c507b35645)

Solutions

  1. Raise fd and thread limits for the host process (ulimit -n, ulimit -u, container limits)
  2. If sandboxed, allow epoll_create1, epoll_ctl, epoll_wait and timerfd_create syscalls in the security profile
  3. Check /proc/<pid>/fd counts and thread counts to identify exhaustion
  4. Reduce the number of concurrently running runtimes if the host creates many TokioSpawners

Example fix

// before
.build()
.expect("failed to construct async-message tokio runtime")

// after
.build()
.unwrap_or_else(|e| panic!("failed to construct async-message tokio runtime: {e}"))
Defensive patterns

Strategy: try-catch

Try / catch

let runtime = tokio::runtime::Builder::new_multi_thread()
	.worker_threads(1)
	.thread_name("graphite-async")
	.enable_all()
	.build();
let runtime = match runtime {
	Ok(rt) => rt,
	Err(e) => panic!("failed to construct async-message tokio runtime: {e} (check thread/fd limits and sandbox syscalls)"),
};

Prevention

When it happens

Trigger: Constructing the editor's async spawner in a process at its fd or thread limit; running under a strict seccomp/container profile that blocks epoll or timerfd syscalls; severe memory pressure preventing the worker thread's stack allocation.

Common situations: Embedding the editor crate inside sandboxed executors (gVisor, firecracker, restricted gVisor-like runtimes) that block the I/O driver syscalls; apps that exhaust file descriptors before spawning async work; test harnesses with low rlimits.

Related errors


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