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
- Set an absolute, verified shell path in config.kdl, e.g. default-shell "/bin/bash"
- Check `command -v <cmd>` resolves in the server's environment - it daemonizes and may not inherit your interactive PATH
- Confirm the binary is executable (chmod +x) and matches the host architecture
- 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
- Use absolute paths for default-shell and layout commands
- Verify command availability in the server's environment, not your interactive shell
- Make pane-spawn failures per-pane errors instead of thread-fatal panics
- Keep layouts versioned alongside a check that all referenced binaries exist
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
- could not daemonize the server process
- failed to receive event on channel
- 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/4cd46782bf520cc6.
Report an issue: GitHub.