From 3ccef20ef4e01875762ccfe4fbe0ee34f86a4a9e Mon Sep 17 00:00:00 2001 From: jif Date: Wed, 24 Jun 2026 20:59:53 +0100 Subject: [PATCH] Skip credential refresh for WindowsApps launch failures (#29637) ## Summary - keep the child error 1312 credential retry for normal executables - return WindowsApps/AppX launch errors directly instead of rotating sandbox credentials and retrying the same command ## Why Windows AppX activation can return `ERROR_NO_SUCH_LOGON_SESSION` (1312) even when the sandbox token is healthy. For executables under `WindowsApps`, refreshing the sandbox account password cannot fix that activation failure; it only triggers elevated setup before the same command fails again. This is a focused follow-up to #29624. --- .../src/elevated/runner_client.rs | 52 ++++++++++++++++--- .../windows-sandbox-rs/src/elevated_impl.rs | 1 + .../src/unified_exec/backends/elevated.rs | 1 + 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs index 1d5ea9297..29fe0d7a0 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs @@ -102,7 +102,18 @@ fn is_refreshable_windows_error(code: u32) -> bool { matches!(code, ERROR_LOGON_FAILURE | ERROR_NO_SUCH_LOGON_SESSION) } -pub(crate) fn is_refreshable_sandbox_creds_error(err: &anyhow::Error) -> bool { +fn command_targets_windows_apps(command: &[String]) -> bool { + command.first().is_some_and(|program| { + Path::new(program).components().any(|component| { + component + .as_os_str() + .to_string_lossy() + .eq_ignore_ascii_case("WindowsApps") + }) + }) +} + +pub(crate) fn is_refreshable_sandbox_creds_error(err: &anyhow::Error, command: &[String]) -> bool { if err .downcast_ref::() .is_some_and(|err| is_refreshable_windows_error(err.code)) @@ -112,21 +123,25 @@ pub(crate) fn is_refreshable_sandbox_creds_error(err: &anyhow::Error) -> bool { err.downcast_ref::().is_some_and(|err| { err.payload.stage == ErrorStage::SpawnChild - && err - .payload - .windows_error_code - .is_some_and(is_refreshable_windows_error) + && err.payload.windows_error_code.is_some_and(|code| { + // AppX activation can return 1312 for a healthy sandbox token. Rotating the + // account password cannot make the same WindowsApps command launch. + is_refreshable_windows_error(code) + && (code != ERROR_NO_SUCH_LOGON_SESSION + || !command_targets_windows_apps(command)) + }) }) } pub(crate) fn retry_runner_spawn_once( sandbox_creds: SandboxCreds, + command: &[String], mut spawn: impl FnMut(SandboxCreds) -> Result, refresh: impl FnOnce() -> Result, ) -> Result { match spawn(sandbox_creds) { Ok(result) => Ok(result), - Err(err) if is_refreshable_sandbox_creds_error(&err) => spawn(refresh()?), + Err(err) if is_refreshable_sandbox_creds_error(&err, command) => spawn(refresh()?), Err(err) => Err(err), } } @@ -494,7 +509,7 @@ mod tests { .map(|code| { let err = anyhow::Error::new(RunnerLogonError { code }).context("runner launch failed"); - is_refreshable_sandbox_creds_error(&err) + is_refreshable_sandbox_creds_error(&err, &[]) }), [true, true, false] ); @@ -511,9 +526,30 @@ mod tests { stage, windows_error_code: Some(windows_error_code), })); - is_refreshable_sandbox_creds_error(&err) + is_refreshable_sandbox_creds_error(&err, &["cmd.exe".to_string()]) }), [true, false, false] ); + + let windows_apps_commands = [ + vec![ + r"C:\Users\user\AppData\Local\Microsoft\WindowsApps\pwsh.exe".to_string(), + ], + vec![ + r"C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.3.0_x64__8wekyb3d8bbwe\pwsh.exe" + .to_string(), + ], + ]; + assert_eq!( + windows_apps_commands.map(|command| { + let err = anyhow::Error::new(RunnerStartupError::new(ErrorPayload { + message: "runner startup failed".to_string(), + stage: ErrorStage::SpawnChild, + windows_error_code: Some(ERROR_NO_SUCH_LOGON_SESSION), + })); + is_refreshable_sandbox_creds_error(&err, &command) + }), + [false, false] + ); } } diff --git a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs index ccda7429f..5475b9542 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs @@ -197,6 +197,7 @@ mod windows_impl { }; let transport = retry_runner_spawn_once( sandbox_creds, + &spawn_request.command, |sandbox_creds| { spawn_runner_transport( codex_home, diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs index 4fc07d9c4..7270fe6d6 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs @@ -63,6 +63,7 @@ fn spawn_runner_transport_with_retry( ) -> Result { retry_runner_spawn_once( sandbox_creds, + &request.spawn_request.command, |sandbox_creds| { spawn( &request.codex_home,