{"record":{"id":"9fc03d0f269ffcff","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-cli-executable-app-server","errorCode":null,"errorMessage":"Codex CLI 没有执行权限：{executable}","messagePattern":"Codex CLI 没有执行权限：(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/connect/app_server.rs","lineNumber":88,"sourceCode":"        bail!(\n            \"Codex CLI 路径不存在：{executable}\\n请填 Codex CLI 可执行文件的完整路径。\\\n             macOS 桌面版通常在 /Applications/ChatGPT.app/Contents/Resources/codex；\\\n             npm 全局安装可用 `which codex` / `where codex` 查看。\"\n        );\n    }\n    if path.is_dir() {\n        bail!(\n            \"Codex CLI 路径指向的是目录而不是可执行文件：{executable}\\n\\\n             请去掉结尾的路径分隔符，或补上具体的可执行文件名。\"\n        );\n    }\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::PermissionsExt;\n        if let Ok(metadata) = std::fs::metadata(path)\n            && metadata.permissions().mode() & 0o111 == 0\n        {\n            bail!(\"Codex CLI 没有执行权限：{executable}\");\n        }\n    }\n    Ok(())\n}\n\nimpl CodexAppServer {\n    pub async fn start(config: AppServerConfig) -> anyhow::Result<Self> {\n        let executable = if config.executable.trim().is_empty() {\n            \"codex\"\n        } else {\n            config.executable.trim()\n        };\n        validate_codex_executable(executable)?;\n        let mut command = Command::new(executable);\n        command\n            .arg(\"app-server\")\n            .current_dir(&config.work_dir)\n            .stdin(Stdio::piped())","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/connect/app_server.rs#L70-L106","documentation":"On Unix, validate_codex_executable checks the file mode bits; if none of the execute bits (0o111) are set on the Codex CLI binary, the library throws this error before spawn. This prevents a confusing spawn PermissionDenied later by naming the real cause: the file exists but is not executable.","triggerScenarios":"CodexAppServer::start with executable pointing to an existing regular file whose permission bits lack any x bit — e.g. a binary restored from a zip/tar without preserving modes, or copied with cp -r after a restrictive umask.","commonSituations":"Extracted codex binary from an archive (modes lost), mounted a volume with noexec... actually with restrictive umask, downloaded binary without chmod +x, files synced via cloud tools that drop permissions.","solutions":["Run `chmod +x <path-to-codex>` to add the execute bit and retry","Reinstall via the package manager (npm i -g @openai/codex or the installer) so permissions are set correctly","If on a filesystem that strips modes, move the binary to a local filesystem (e.g. ~/bin)"],"exampleFix":"// shell, before\n-rw-r--r-- codex\n// after\nchmod +x /usr/local/bin/codex","handlingStrategy":"validation","validationCode":"#[cfg(unix)]\nfn has_exec_bit(path: &str) -> bool {\n    use std::os::unix::fs::PermissionsExt;\n    std::fs::metadata(path)\n        .map(|m| m.permissions().mode() & 0o111 != 0)\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match CodexAppServer::start(config).await {\n    Err(e) if e.to_string().contains(\"没有执行权限\") => {\n        eprintln!(\"chmod +x {} and retry\", config.executable);\n    }\n    r => r?,\n}","preventionTips":["chmod +x after extracting binaries from archives","Install via a package manager (npm/homebrew) so permissions are set automatically","Check with `ls -l` that the binary has x bits before configuring it","Avoid storing binaries on filesystems that drop permission bits"],"tags":["permissions","filesystem","unix","validation"],"backgroundTag":"permission-denied","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}