zellij-org/zellij · critical

failed to spawn

Error message

failed to spawn

What it means

The unix pty spawn: after preparing the child with login_tty on the secondary pty and closing inherited fds in pre_exec, Command::spawn() is expected to succeed. It fails when the requested program cannot be executed - ENOENT for a missing command, EACCES for a non-executable file - or when fork/pty allocation fails under resource limits. The panic kills the pty thread, and with it the server.

Source

Thrown at zellij-server/src/os_input_output_unix.rs:235

            } else {
                log::error!(
                    "Failed to set CWD for new pane. '{}' does not exist or is not a folder",
                    current_dir.display()
                );
            }
        }
        command
            .args(&cmd.args)
            .env("ZELLIJ_PANE_ID", &format!("{}", terminal_id))
            .pre_exec(move || -> io::Result<()> {
                if libc::login_tty(pid_secondary) != 0 {
                    panic!("failed to set controlling terminal");
                }
                close_fds::close_open_fds(3, &[]);
                Ok(())
            })
            .spawn()
            .expect("failed to spawn")
    };

    let child_id = child.id();
    thread::spawn(move || {
        child.wait().with_context(|| err_context(&cmd)).fatal();
        let exit_status = handle_command_exit(child)
            .with_context(|| err_context(&cmd))
            .fatal();
        let _ = unistd::close(pid_secondary);
        quit_cb(PaneId::Terminal(terminal_id), exit_status, cmd);
    });

    Ok((pid_primary, child_id as RawFd))
}

/// Spawns a new terminal from the parent terminal with [`termios`](termios::Termios)
/// `orig_termios`.
fn handle_terminal(

View on GitHub (pinned to 98a0837077)

Solutions

  1. Set an absolute, verified shell path in config.kdl, e.g. default-shell "/bin/bash"
  2. Check `command -v <cmd>` resolves in the server's environment - it daemonizes and may not inherit your interactive PATH
  3. Confirm the binary is executable (chmod +x) and matches the host architecture
  4. Raise process/fd limits if spawn fails only under heavy pane churn

Example fix

// before
command
    .args(&cmd.args)
    // ... pre_exec with login_tty ...
    .spawn()
    .expect("failed to spawn");

// after - validate the binary first, then surface spawn errors with context
let program = &cmd.command;
if which::which(program).is_err() {
    log::error!("command not found: {program}");
    quit_cb(PaneId::Terminal(terminal_id), None, cmd);
    return;
}
let child = command.spawn().with_context(|| err_context(&cmd)).fatal();
Defensive patterns

Strategy: validation

Validate before calling

// before spawning a pane command
fn spawnable(cmd: &str) -> bool {
    let p = std::path::Path::new(cmd);
    if p.components().count() > 1 {
        p.exists() // explicit path: check directly
    } else {
        // bare name: search PATH the way the daemonized server would
        std::env::var_os("PATH")
            .map(|paths| std::env::split_paths(&paths).any(|dir| dir.join(cmd).exists()))
            .unwrap_or(false)
    }
}

Try / catch

match command.spawn() {
    Ok(child) => child,
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
        log::error!("{}: command not found", cmd.command);
        quit_cb(PaneId::Terminal(terminal_id), None, cmd);
        return;
    }
    Err(e) => {
        log::error!("spawn failed: {e}");
        quit_cb(PaneId::Terminal(terminal_id), None, cmd);
        return;
    }
}

Prevention

When it happens

Trigger: default-shell or a layout command referencing a nonexistent binary; the binary exists but lacks the execute bit or has the wrong architecture; ENOMEM/EAGAIN at fork; pty pairs exhausted.

Common situations: Misspelled default-shell in config.kdl; per-user shells (nix store, homebrew) the daemonized server cannot resolve through its reduced PATH; layouts spawning tools that are not installed; containers restricting pseudo-tty allocation.

Related errors


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