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

  1. Launch zellij from a stable existing directory such as $HOME
  2. Raise the process limit (ulimit -u / systemd TasksMax) on the host
  3. Verify /dev/null exists as character device 1:3 with mode 666; recreate it if broken
  4. 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

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


AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16). Data as JSON: /api/errors/2cd3cd1636722b65. Report an issue: GitHub.