{"record":{"id":"2efafe728326c2e7","repo":"openai/codex","slug":"zsh-fork-feature-enabled-but-packaged-zsh-fork","errorCode":null,"errorMessage":"zsh fork feature enabled, but packaged zsh fork `{}` is not usable","messagePattern":"zsh fork feature enabled, but packaged zsh fork `(.+?)` is not usable","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/core/src/session/session.rs","lineNumber":1158,"sourceCode":"            let use_zsh_fork_shell = config.features.enabled(Feature::ShellZshFork);\n            let default_shell = if let Some(user_shell_override) =\n                session_configuration.user_shell_override.clone()\n            {\n                user_shell_override\n            } else if use_zsh_fork_shell {\n                let zsh_path = config.zsh_path.as_ref().ok_or_else(|| {\n                    anyhow::anyhow!(\n                        \"zsh fork feature enabled, but no packaged zsh fork is available for this install\"\n                    )\n                })?;\n                if zsh_path.is_file() {\n                    shell::Shell {\n                        shell_type: shell::ShellType::Zsh,\n                        shell_path: zsh_path.clone(),\n                    }\n                } else {\n                    shell::get_shell(shell::ShellType::Zsh).ok_or_else(|| {\n                        anyhow::anyhow!(\n                            \"zsh fork feature enabled, but packaged zsh fork `{}` is not usable\",\n                            zsh_path.display()\n                        )\n                    })?\n                }\n            } else {\n                shell::default_user_shell()\n            };\n            let use_executor_shell_snapshots = config.features.enabled(Feature::ShellSnapshotV2)\n                && config.features.enabled(Feature::ShellTool)\n                && config.features.enabled(Feature::UnifiedExec)\n                && matches!(\n                    codex_tools::UnifiedExecShellMode::for_session(\n                        config.features.get(),\n                        crate::tools::tool_user_shell_type(&default_shell),\n                        config.zsh_path.as_ref(),\n                        config.main_execve_wrapper_exe.as_ref(),\n                    ),","sourceCodeStart":1140,"sourceCodeEnd":1176,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/core/src/session/session.rs#L1140-L1176","documentation":"Codex fails during session construction (session.rs) when the experimental zsh fork shell feature (Feature::ShellZshFork) is enabled in config.features. The code first requires config.zsh_path to be Some (a separate error covers the None case), then checks zsh_path.is_file(); if the packaged zsh binary is missing it falls back to shell::get_shell(ShellType::Zsh), which searches the system for zsh. This error means BOTH failed: the packaged zsh fork at the printed path is not a usable file AND no system zsh could be located. The session cannot start until the shell is resolvable.","triggerScenarios":"Enabling the zsh fork feature flag in config (e.g. [features] shell_zsh_fork) while (a) the install's packaged zsh fork binary at config.zsh_path has been deleted, moved, corrupted, or never downloaded, AND (b) no zsh exists on PATH so get_shell(ShellType::Zsh) returns None.","commonSituations":"Partial or interrupted install/update that dropped the bundled zsh fork; custom CODEX_HOME pointing at a directory that lacks the zsh payload; dev builds that never bundle zsh; macOS quarantine (com.apple.quarantine) or missing execute bit making the file unusable; sandboxed/scrubbed environments where PATH has no zsh.","solutions":["Check the exact path printed in the error (ls -l <path>) and reinstall or update the Codex install so the packaged zsh fork exists there","If the file exists, fix usability: chmod +x it, and on macOS run xattr -d com.apple.quarantine <path>","Install a system zsh (brew install zsh / apt install zsh) so the get_shell(ShellType::Zsh) fallback succeeds","If neither is possible, disable the feature by removing the zsh fork flag from features config so the default user shell is used"],"exampleFix":"// config.toml — before\n[features]\nshell_zsh_fork = true\n\n// after (no packaged/system zsh available)\n[features]\n# shell_zsh_fork = true  // disabled; fall back to default_user_shell()","handlingStrategy":"validation","validationCode":"// Run before session construction / before enabling the zsh fork feature\nfn zsh_fork_ready(cfg_zsh_path: Option<&std::path::Path>) -> bool {\n    match cfg_zsh_path {\n        Some(p) if p.is_file() => true,\n        _ => codex_core::shell::get_shell(codex_core::shell::ShellType::Zsh).is_some(),\n    }\n}","typeGuard":"fn usable_zsh_available(zsh_path: Option<&std::path::Path>) -> bool {\n    zsh_path.is_some_and(|p| p.is_file())\n        || codex_core::shell::get_shell(codex_core::shell::ShellType::Zsh).is_some()\n}","tryCatchPattern":"// Session construction returns anyhow::Result — surface it, don't unwrap:\nmatch Session::new(config).await {\n    Err(e) if e.to_string().contains(\"zsh fork\") => { /* disable feature or guide install */ }\n    other => other?,\n}","preventionTips":["Verify the packaged zsh fork file exists and is executable after every install/update","Keep a system zsh on PATH (brew/apt) so the get_shell fallback can rescue a missing packaged binary","Gate experimental feature flags in config behind a preflight check in test/CI environments","Don't enable shell_zsh_fork in shared configs for machines whose installs don't bundle the zsh payload"],"tags":["rust","codex","shell","zsh","feature-flag","session-startup"],"backgroundTag":"missing-executable-path","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}