elkowar/eww · critical
Failed to initialize tokio runtime
Error message
Failed to initialize tokio runtime
What it means
tokio's multi-threaded runtime Builder::build() returned an Err, and eww unwraps it with expect. Runtime construction fails almost exclusively when the OS refuses to create the runtime's worker/blocking threads (thread spawn failure), e.g. due to resource limits.
Solutions
- Check and raise limits: `ulimit -u`, systemd `TasksMax=infinity` on the user unit, container pids cgroup limit.
- Free resources: kill leaked eww/zombie processes of the same user, or restart the session.
- Run eww with fewer competing processes, or in a container with raised limits.
- As a code-level guard, replace .expect with propagating the error so the daemon can log a clear message and exit gracefully.
Example fix
// before
let rt = tokio::runtime::Builder::new_multi_thread()
.thread_name("main-async-runtime")
.enable_all()
.build()
.expect("Failed to initialize tokio runtime");
// after
let rt = tokio::runtime::Builder::new_multi_thread()
.thread_name("main-async-runtime")
.enable_all()
.worker_threads(2)
.build()
.map_err(|e| anyhow::anyhow!("Failed to initialize tokio runtime: {} (check RLIMIT_NPROC / TasksMax)", e))?; Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check resource headroom let limits = rlimit::getrlimit(rlimit::Resource::NPROC).unwrap(); assert!(limits.0 > 64, "process/thread limit too low for tokio runtime");
Try / catch
match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
Ok(rt) => /* proceed */,
Err(e) => log::error!("runtime init failed: {} — check RLIMIT_NPROC/TasksMax", e),
} Prevention
- Raise ulimit -u and systemd TasksMax for the user session
- Avoid containers with very low pids limits
- Monitor thread counts of long-running eww processes
- Reduce worker_threads on constrained systems
When it happens
Trigger: Calling init_async_part when the process has hit RLIMIT_NPROC, the system is out of memory or PIDs (threads are internally the same resource as processes on Linux), or running inside a container/pod with a low pids cgroup limit.
Common situations: Docker/Kubernetes pods with low pids.max, shared servers where the user already runs many threads, systemd units lacking TasksMax headroom, or memory exhaustion on small VPSes.
Related errors
- Failed to start outer-main-async-runtime thread
- Failed to initialize tokio runtime
- Failed to start command-execution-thread
AI-assisted analysis of elkowar/eww@48f5aa8b37 (2026-09-08).
Data as JSON: /api/errors/02a667e21e965141.
Report an issue: GitHub.
Appendix: source
Thrown at crates/eww/src/server.rs:169
fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()> {
let (daemon_resp_sender, mut daemon_resp_response) = daemon_response::create_pair();
ui_send.send(DaemonCommand::ReloadConfigAndCss(daemon_resp_sender))?;
tokio::spawn(async move {
match daemon_resp_response.recv().await {
Some(daemon_response::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"),
Some(daemon_response::DaemonResponse::Failure(e)) => eprintln!("{}", e),
None => log::error!("No response to reload configuration-reload request"),
}
});
Ok(())
}
fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>) -> tokio::runtime::Handle {
let rt = tokio::runtime::Builder::new_multi_thread()
.thread_name("main-async-runtime")
.enable_all()
.build()
.expect("Failed to initialize tokio runtime");
let handle = rt.handle().clone();
std::thread::Builder::new()
.name("outer-main-async-runtime".to_string())
.spawn(move || {
rt.block_on(async {
let filewatch_join_handle = {
let ui_send = ui_send.clone();
let paths = paths.clone();
tokio::spawn(async move { run_filewatch(paths.config_dir, ui_send).await })
};
let ipc_server_join_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move { ipc_server::run_server(ui_send, paths.get_ipc_socket_file()).await })
};
let forward_exit_to_app_handle = {View on GitHub (pinned to 48f5aa8b37)