diff --git a/codex-rs/core/src/windows_sandbox.rs b/codex-rs/core/src/windows_sandbox.rs index 7b9451c24610..9a34a9f90f78 100644 --- a/codex-rs/core/src/windows_sandbox.rs +++ b/codex-rs/core/src/windows_sandbox.rs @@ -65,6 +65,25 @@ pub fn elevated_setup_failure_details(_err: &anyhow::Error) -> Option<(String, S None } +#[cfg(target_os = "windows")] +pub fn elevated_setup_failure_metric_name(err: &anyhow::Error) -> &'static str { + if codex_windows_sandbox::extract_setup_failure(err).is_some_and(|failure| { + matches!( + failure.code, + codex_windows_sandbox::SetupErrorCode::OrchestratorHelperLaunchCanceled + ) + }) { + "codex.windows_sandbox.elevated_setup_canceled" + } else { + "codex.windows_sandbox.elevated_setup_failure" + } +} + +#[cfg(not(target_os = "windows"))] +pub fn elevated_setup_failure_metric_name(_err: &anyhow::Error) -> &'static str { + panic!("elevated_setup_failure_metric_name is only supported on Windows") +} + #[cfg(target_os = "windows")] pub fn run_elevated_setup( policy: &SandboxPolicy, diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 0e155fdc0090..fb80940a94e0 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -1700,7 +1700,9 @@ impl App { tags.push(("message", message)); } otel_manager.counter( - "codex.windows_sandbox.elevated_setup_failure", + codex_core::windows_sandbox::elevated_setup_failure_metric_name( + &err, + ), 1, &tags, ); diff --git a/codex-rs/windows-sandbox-rs/src/setup_error.rs b/codex-rs/windows-sandbox-rs/src/setup_error.rs index 21d260b4d439..9ee9ebf7f574 100644 --- a/codex-rs/windows-sandbox-rs/src/setup_error.rs +++ b/codex-rs/windows-sandbox-rs/src/setup_error.rs @@ -22,6 +22,8 @@ pub enum SetupErrorCode { OrchestratorPayloadSerializeFailed, /// Failed to launch the setup helper process (spawn or ShellExecuteExW). OrchestratorHelperLaunchFailed, + /// User canceled the UAC prompt while launching the helper. + OrchestratorHelperLaunchCanceled, /// Helper exited non-zero and no structured report was available. OrchestratorHelperExitNonzero, /// Helper exited non-zero and reading `setup_error.json` failed. @@ -72,6 +74,7 @@ impl SetupErrorCode { Self::OrchestratorElevationCheckFailed => "orchestrator_elevation_check_failed", Self::OrchestratorPayloadSerializeFailed => "orchestrator_payload_serialize_failed", Self::OrchestratorHelperLaunchFailed => "orchestrator_helper_launch_failed", + Self::OrchestratorHelperLaunchCanceled => "orchestrator_helper_launch_canceled", Self::OrchestratorHelperExitNonzero => "orchestrator_helper_exit_nonzero", Self::OrchestratorHelperReportReadFailed => "orchestrator_helper_report_read_failed", Self::HelperRequestArgsFailed => "helper_request_args_failed", diff --git a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs index 74137379ecd1..78e6d566fbbf 100644 --- a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs +++ b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs @@ -34,6 +34,7 @@ use windows_sys::Win32::Security::SECURITY_NT_AUTHORITY; pub const SETUP_VERSION: u32 = 5; pub const OFFLINE_USERNAME: &str = "CodexSandboxOffline"; pub const ONLINE_USERNAME: &str = "CodexSandboxOnline"; +const ERROR_CANCELLED: u32 = 1223; const SECURITY_BUILTIN_DOMAIN_RID: u32 = 0x0000_0020; const DOMAIN_ALIAS_RID_ADMINS: u32 = 0x0000_0220; @@ -411,8 +412,13 @@ fn run_setup_exe( let ok = unsafe { ShellExecuteExW(&mut sei) }; if ok == 0 || sei.hProcess == 0 { let last_error = unsafe { GetLastError() }; + let code = if last_error == ERROR_CANCELLED { + SetupErrorCode::OrchestratorHelperLaunchCanceled + } else { + SetupErrorCode::OrchestratorHelperLaunchFailed + }; return Err(failure( - SetupErrorCode::OrchestratorHelperLaunchFailed, + code, format!("ShellExecuteExW failed to launch setup helper: {last_error}"), )); }