zellij-org/zellij · critical
could not daemonize the server process
Error message
could not daemonize the server process
What it means
On unix the zellij server daemonizes before binding its sockets: fork, setsid, redirect stdio to /dev/null and chdir to the current working directory, explicitly preserving the inherited umask. The expect fires if any step fails - fork(2) EAGAIN at the process limit, chdir into a directory deleted after launch, or failure opening /dev/null. This happens during server bootstrap, so a new session cannot start.
Source
Thrown at zellij-server/src/lib.rs:864
// After clear, removing the client yields no stuck tokens.
assert!(s.remove_client(1).is_empty());
}
}
pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
info!("Starting Zellij server!");
#[cfg(unix)]
{
use nix::sys::stat::{umask, Mode};
// preserve the current umask: read current value by setting to another mode, and then restoring it
let current_umask = umask(Mode::all());
umask(current_umask);
daemonize::Daemonize::new()
.working_directory(std::env::current_dir().unwrap())
.umask(current_umask.bits() as u32)
.start()
.expect("could not daemonize the server process");
}
#[cfg(windows)]
{
// The server is spawned with CREATE_NEW_PROCESS_GROUP, which disables
// Ctrl+C handling for the process. Child processes inherit this
// disabled state, so ConPTY children (shells, commands) would silently
// ignore CTRL_C_EVENT signals. Re-enable Ctrl+C here so that
// descendants get the normal default handler (terminate on Ctrl+C).
//
use windows_sys::Win32::System::Console::SetConsoleCtrlHandler;
unsafe {
SetConsoleCtrlHandler(None, 0);
}
}
start_server_impl(os_input, socket_path, true);
}View on GitHub (pinned to 98a0837077)
Solutions
- Launch zellij from a stable existing directory such as $HOME
- Raise the process limit (ulimit -u / systemd TasksMax) on the host
- Verify /dev/null exists as character device 1:3 with mode 666; recreate it if broken
- Retry session creation after fixing the environment; each start re-attempts daemonize
Example fix
// before
daemonize::Daemonize::new()
.working_directory(std::env::current_dir().unwrap())
.umask(current_umask.bits() as u32)
.start()
.expect("could not daemonize the server process");
// after - validate preconditions and fail with context
let cwd = std::env::current_dir().context("no current dir").fatal();
std::fs::metadata(&cwd).context("cwd vanished before daemonize").fatal();
daemonize::Daemonize::new()
.working_directory(cwd)
.umask(current_umask.bits() as u32)
.start()
.context("daemonize failed: fork limit, cwd, or /dev/null")
.fatal(); Defensive patterns
Strategy: validation
Validate before calling
// run before daemonizing
use std::os::unix::fs::FileTypeExt;
fn daemonize_preconditions_ok() -> bool {
let cwd_ok = std::env::current_dir().map(|p| p.exists()).unwrap_or(false);
let devnull_ok = std::fs::metadata("/dev/null")
.map(|m| m.file_type().is_char_device())
.unwrap_or(false);
cwd_ok && devnull_ok
} Prevention
- Always start sessions from long-lived directories such as $HOME
- Keep /dev/null intact in minimal containers (devtmpfs mounted)
- Set TasksMax/RLIMIT_NPROC with headroom for the fork
- Fail fast with a clear message if preconditions fail instead of expecting inside daemonize
When it happens
Trigger: Starting or resuming a session when the launch directory no longer exists (ENOENT on chdir), the process limit blocks fork (EAGAIN), or /dev/null is missing/unwritable.
Common situations: Launching zellij from a temp dir, an unmounted share, or a deleted directory; heavily loaded build machines; containers with strict process limits or a broken /dev/null.
Related errors
- failed to spawn
- 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}
- no active pane for client {client_id}
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/2cd3cd1636722b65.
Report an issue: GitHub.