{"record":{"id":"5603e8e0eb0abdd5","repo":"BigPizzaV3/CodexPlusPlus","slug":"dmg-error","errorCode":null,"errorMessage":"打开 DMG 失败：{error}","messagePattern":"打开 DMG 失败：(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/update.rs","lineNumber":427,"sourceCode":"\npub fn launch_installer(path: &Path) -> anyhow::Result<()> {\n    #[cfg(windows)]\n    {\n        use std::os::windows::process::CommandExt;\n        std::process::Command::new(path)\n            .creation_flags(crate::windows_integration::CREATE_NO_WINDOW)\n            .spawn()\n            .map(|_| ())\n            .map_err(|error| anyhow::anyhow!(\"启动安装包失败：{error}\"))\n    }\n\n    #[cfg(target_os = \"macos\")]\n    {\n        std::process::Command::new(\"open\")\n            .arg(path)\n            .spawn()\n            .map(|_| ())\n            .map_err(|error| anyhow::anyhow!(\"打开 DMG 失败：{error}\"))\n    }\n\n    #[cfg(all(not(windows), not(target_os = \"macos\")))]\n    {\n        let _ = path;\n        anyhow::bail!(\"当前平台不支持启动安装包\")\n    }\n}\n","sourceCodeStart":409,"sourceCodeEnd":436,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/update.rs#L409-L436","documentation":"On macOS, launch_installer opens the downloaded .dmg via the system 'open' command. If spawning 'open' fails, the io error is mapped to 打开 DMG 失败：{error}. Note that on any non-Windows, non-macOS platform the function instead throws the unrelated '当前平台不支持启动安装包' error.","triggerScenarios":"Calling perform_update on macOS when: the 'open' binary is missing from PATH (rare, broken/minimal environment), spawn fails due to resource limits, or the downloaded path was removed before launch. The .dmg file itself opening-then-failing in Finder is NOT this error — only the spawn failure is.","commonSituations":"Running the app in a sandboxed/odd environment without /usr/bin/open; installer file deleted by a cleaner between download and launch; disk-full or fork-limit conditions preventing process creation.","solutions":["Confirm /usr/bin/open exists and works: run `open` from a terminal.","Verify the .dmg still exists at the reported installer_path; re-download via perform_update if a cleaner removed it.","Open the .dmg manually with Finder or `open <path>` to complete the update.","Retry perform_update — spawn failures from transient resource limits usually clear.","If 'open' is genuinely unavailable, mount the .dmg with hdiutil attach and copy the app manually."],"exampleFix":"// before\nlet install = perform_update(&release, &dir).await?; // panics flow on 打开 DMG 失败\n// after\nmatch perform_update(&release, &dir).await {\n    Ok(i) => ..., // i.launched == true on success\n    Err(e) if e.to_string().contains(\"打开 DMG 失败\") => {\n        eprintln!(\"auto-open failed: {e}; mount {} with hdiutil/Finder\", expected_dmg.display());\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":"fn dmg_openable(path: &std::path::Path) -> bool {\n    path.is_file()\n        && std::process::Command::new(\"open\")\n            .arg(\"--help\")\n            .output()\n            .map(|o| o.status.success())\n            .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match perform_update(&release, &dir).await {\n    Ok(install) => Ok(install),\n    Err(e) if e.to_string().contains(\"打开 DMG 失败\") => {\n        eprintln!(\"auto-open failed: {e}; mount {} via Finder or `hdiutil attach`\", expected_dmg.display());\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Don't remove the download directory between download and launch — cleaners can race the updater.","Keep the dmg path intact and verify it exists before invoking the update flow.","On macOS ensure the process environment can spawn /usr/bin/open (avoid broken PATH in launchd contexts).","Provide the dmg path in the error UI so users can finish the install manually."],"tags":["macos","dmg","process-spawn","installer","update"],"backgroundTag":"command-not-found","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"}