zellij-org/zellij · critical
Failed to create tokio runtime
Error message
Failed to create tokio runtime
What it means
The server's process-global async runtime: a multi-thread tokio runtime with 4 named workers and all features enabled, created once via OnceCell and shared by plugin downloads, timers and action-completion tracking. Construction allocates worker threads and registers the I/O + time drivers, so it fails under thread/memory limits (EAGAIN, ENOMEM) or fd exhaustion when registering drivers. Since every async path in the server funnels through this static, failure aborts the server at first use.
Source
Thrown at zellij-server/src/global_async_runtime.rs:15
use once_cell::sync::OnceCell;
use tokio::runtime::Runtime;
// Global tokio runtime for async I/O operations
// Shared between plugin downloads, timers, and action completion tracking
static TOKIO_RUNTIME: OnceCell<Runtime> = OnceCell::new();
pub fn get_tokio_runtime() -> &'static Runtime {
TOKIO_RUNTIME.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.thread_name("async-runtime")
.enable_all()
.build()
.expect("Failed to create tokio runtime")
})
}
View on GitHub (pinned to 98a0837077)
Solutions
- Raise RLIMIT_NPROC, RLIMIT_NOFILE and memory limits for the zellij server
- Restart the session/server after freeing resources; the runtime is per-process
- Reduce plugin count or disable heavyweight plugins to lower baseline usage
- Check kernel logs (dmesg) for OOM or thread-creation refusals if it recurs
Example fix
// before
tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.thread_name("async-runtime")
.enable_all()
.build()
.expect("Failed to create tokio runtime");
// after - degrade to a current-thread runtime rather than dying
let rt = match tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.thread_name("async-runtime")
.enable_all()
.build()
{
Ok(rt) => rt,
Err(e) => {
log::warn!("multi-thread runtime failed ({e}); using current-thread fallback");
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("fallback runtime also failed")
.fatal()
}
}; Defensive patterns
Strategy: fallback
Try / catch
let rt = match tokio::runtime::Builder::new_multi_thread().worker_threads(4).enable_all().build() {
Ok(rt) => rt,
Err(e) => {
log::warn!("multi-thread runtime failed ({e}); using current-thread fallback");
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("fallback runtime also failed")
.fatal()
}
}; Prevention
- Provision the server host for 4+ extra worker threads plus I/O driver fds
- Create the runtime during server startup, not lazily mid-session, so failures are actionable
- Cap plugin concurrency so the shared runtime is never the resource bottleneck
- Alert on thread-count and fd-count growth of the server process
When it happens
Trigger: The first get_tokio_runtime() call - a plugin starting a download, a timer being scheduled, or action-completion tracking - on a host already at thread, fd or memory limits.
Common situations: Zellij servers on constrained containers; hosts with low RLIMIT_NPROC/RLIMIT_NOFILE; memory pressure from large scrollbacks or many plugins.
Related errors
- failed to build forward-timeout runtime
- failed to spawn forward-timeout driver thread
- Could not find editor pane to replace - is no pane focused?
- Could not find editor pane to replace
- failed to find active pane id for client {client_id}
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/42d713161943791c.
Report an issue: GitHub.