BigPizzaV3/CodexPlusPlus · error · anyhow::Error

启动安装包失败:{error}

Error message

启动安装包失败:{error}

What it means

On Windows, launch_installer spawns the downloaded installer with std::process::Command::new(path) (using CREATE_NO_WINDOW). If the OS refuses to spawn — file missing, not an executable, permission denied, AV interference — the io::Error is wrapped as "启动安装包失败:{error}". This is spawn failure (the process never started), not a non-zero installer exit code, which launch_installer does not check at all.

Source

Thrown at crates/codex-plus-core/src/update.rs:418

            || name.ends_with("setup.exe")
            || name.ends_with("installer.exe"))
}

fn is_macos_installer_asset(name: &str) -> bool {
    // Loose shape check; arch preference is handled by platform_asset_rank
    // via is_macos_native_arch_asset.
    name.contains("codex") && name.contains("plus") && name.ends_with(".dmg")
}

pub fn launch_installer(path: &Path) -> anyhow::Result<()> {
    #[cfg(windows)]
    {
        use std::os::windows::process::CommandExt;
        std::process::Command::new(path)
            .creation_flags(crate::windows_integration::CREATE_NO_WINDOW)
            .spawn()
            .map(|_| ())
            .map_err(|error| anyhow::anyhow!("启动安装包失败:{error}"))
    }

    #[cfg(target_os = "macos")]
    {
        std::process::Command::new("open")
            .arg(path)
            .spawn()
            .map(|_| ())
            .map_err(|error| anyhow::anyhow!("打开 DMG 失败:{error}"))
    }

    #[cfg(all(not(windows), not(target_os = "macos")))]
    {
        let _ = path;
        anyhow::bail!("当前平台不支持启动安装包")
    }
}

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Verify the path exists and has the expected .exe extension immediately before launching
  2. Re-run perform_update to re-download, then launch without delay
  3. Check antivirus/quarantine history; add an exclusion or code-sign the installer
  4. Keep the download directory alive until the installer has been spawned

Example fix

// before
launch_installer(&install.installer_path)?;

// after
let path = &install.installer_path;
if !path.is_file() {
    anyhow::bail!("installer missing before launch: {}", path.display());
}
launch_installer(path)?;
Defensive patterns

Strategy: try-catch

Validate before calling

let path = &install.installer_path;
if !path.is_file() || path.extension().is_none_or(|e| e != "exe") {
    anyhow::bail!("installer missing or not an exe before launch: {}", path.display());
}
launch_installer(path)?;

Try / catch

match launch_installer(&install.installer_path) {
    Ok(()) => { /* spawned */ }
    Err(err) if err.to_string().contains("启动安装包失败") => {
        // re-download via perform_update and retry the launch once; check AV quarantine if it persists
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: The installer file was deleted between perform_update finishing and launch_installer being called (temp-dir cleanup); the downloaded asset was not actually a .exe (wrong platform asset selected); antivirus/SmartScreen quarantined the binary; the path is on a blocked or UNC location.

Common situations: Apps downloading to a temp dir the OS cleans aggressively; managed devices where AV auto-quarantines unsigned installers; races with cleanup code running right after the update completes.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/bfd51d570a2c23a3. Report an issue: GitHub.