{"record":{"id":"4cd46782bf520cc6","repo":"zellij-org/zellij","slug":"failed-to-spawn","errorCode":null,"errorMessage":"failed to spawn","messagePattern":"failed to spawn","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"zellij-server/src/os_input_output_unix.rs","lineNumber":235,"sourceCode":"            } else {\n                log::error!(\n                    \"Failed to set CWD for new pane. '{}' does not exist or is not a folder\",\n                    current_dir.display()\n                );\n            }\n        }\n        command\n            .args(&cmd.args)\n            .env(\"ZELLIJ_PANE_ID\", &format!(\"{}\", terminal_id))\n            .pre_exec(move || -> io::Result<()> {\n                if libc::login_tty(pid_secondary) != 0 {\n                    panic!(\"failed to set controlling terminal\");\n                }\n                close_fds::close_open_fds(3, &[]);\n                Ok(())\n            })\n            .spawn()\n            .expect(\"failed to spawn\")\n    };\n\n    let child_id = child.id();\n    thread::spawn(move || {\n        child.wait().with_context(|| err_context(&cmd)).fatal();\n        let exit_status = handle_command_exit(child)\n            .with_context(|| err_context(&cmd))\n            .fatal();\n        let _ = unistd::close(pid_secondary);\n        quit_cb(PaneId::Terminal(terminal_id), exit_status, cmd);\n    });\n\n    Ok((pid_primary, child_id as RawFd))\n}\n\n/// Spawns a new terminal from the parent terminal with [`termios`](termios::Termios)\n/// `orig_termios`.\nfn handle_terminal(","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-server/src/os_input_output_unix.rs#L217-L253","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ncommand\n    .args(&cmd.args)\n    // ... pre_exec with login_tty ...\n    .spawn()\n    .expect(\"failed to spawn\");\n\n// after - validate the binary first, then surface spawn errors with context\nlet program = &cmd.command;\nif which::which(program).is_err() {\n    log::error!(\"command not found: {program}\");\n    quit_cb(PaneId::Terminal(terminal_id), None, cmd);\n    return;\n}\nlet child = command.spawn().with_context(|| err_context(&cmd)).fatal();","handlingStrategy":"validation","validationCode":"// before spawning a pane command\nfn spawnable(cmd: &str) -> bool {\n    let p = std::path::Path::new(cmd);\n    if p.components().count() > 1 {\n        p.exists() // explicit path: check directly\n    } else {\n        // bare name: search PATH the way the daemonized server would\n        std::env::var_os(\"PATH\")\n            .map(|paths| std::env::split_paths(&paths).any(|dir| dir.join(cmd).exists()))\n            .unwrap_or(false)\n    }\n}","typeGuard":null,"tryCatchPattern":"match command.spawn() {\n    Ok(child) => child,\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n        log::error!(\"{}: command not found\", cmd.command);\n        quit_cb(PaneId::Terminal(terminal_id), None, cmd);\n        return;\n    }\n    Err(e) => {\n        log::error!(\"spawn failed: {e}\");\n        quit_cb(PaneId::Terminal(terminal_id), None, cmd);\n        return;\n    }\n}","preventionTips":["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"],"tags":["rust","zellij","spawn","pty","unix","command-not-found"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}