Windows sandbox HTTPS via Schannel fails with `SEC_E_NO_CREDENTIALS`
Summary
On Windows, HTTPS requests that use the Windows TLS stack (Schannel) fail inside the current sandboxed shell with:
- PowerShell / .NET:
Authentication failed, see inner exception - inner Win32 error:
SEC_E_NO_CREDENTIALS (0x8009030e) curl.exe(Schannel build):schannel: AcquireCredentialsHandle failed
At the same time:
- Python HTTPS works in the same sandbox
- the same PowerShell HTTPS call works outside the sandbox
- sandbox users can do Schannel HTTPS successfully after their profiles are initialized
I started investigating sandbox-user profile initialization because the Schannel failure was SEC_E_NO_CREDENTIALS, which strongly suggested missing or inaccessible per-user Windows state. The fact that the sandbox users initially had no profile directories made that look like a credible lead.
Related issue: #17458 ("Windows sandbox users are created but their profiles are not initialized until manual first logon").
Version / platform
- Date observed: April 11, 2026
- Platform: Windows 11 / Windows 10.0.26200
- PowerShell: 7.5.5
curl.exe: 8.18.0 withSchannel
Failing behavior
Inside the sandboxed shell:
Invoke-WebRequest -UseBasicParsing -Uri 'https://api.ipify.org'
fails with:
The SSL connection could not be established, see inner exception.
Authentication failed, see inner exception.
SEC_E_NO_CREDENTIALS (0x8009030e)
Raw .NET TLS fails too:
$tcp = [System.Net.Sockets.TcpClient]::new('api.ipify.org',443)
$ssl = [System.Net.Security.SslStream]::new($tcp.GetStream(), $false, ({$true}))
$ssl.AuthenticateAsClient('api.ipify.org')
and curl.exe fails with:
curl: (35) schannel: AcquireCredentialsHandle failed: SEC_E_NO_CREDENTIALS
What works
- Python HTTPS in the same sandbox
- raw TCP connectivity to port 443
- PowerShell HTTPS outside the sandbox
- Schannel HTTPS when run manually as
CodexSandboxOnlineafter profile creation - Schannel HTTPS when run manually as
CodexSandboxOfflineafter profile creation
Important observations
1. The failing current sandboxed shell is running as the real user
Inside the failing shell:
whoami->ELIOT-PC2\EliotUSERPROFILE->C:\Users\Eliot
So the failing shell appears to be a restricted real-user context, not an actual dedicated sandbox-user process.
2. Dedicated sandbox users succeed after profile creation
After manually forcing first logon for:
CodexSandboxOnlineCodexSandboxOffline
both can do:
- PowerShell HTTPS
- raw
.NET SslStream curl.exeHTTPS
successfully.
That manual profile initialization is tracked separately in #17458.
3. There is also an env inheritance bug
In those sandbox-user sessions, env is still inherited from the real user:
USERNAME=EliotUSERPROFILE=C:\Users\EliotAPPDATA=C:\Users\Eliot\AppData\RoamingLOCALAPPDATA=C:\Users\Eliot\AppData\LocalTEMP/TMP=C:\Users\Eliot\AppData\Local\Temp
That looks like a real bug in the elevated Windows sandbox path, but it is not sufficient by itself to explain the Schannel failure, because the sandbox users still succeed despite the wrong env.
Why this looks like a sandbox-specific Schannel issue
The failure is specific to the Windows TLS stack:
- PowerShell / .NET / Schannel
curl.exefail - Python HTTPS succeeds
That points to a Schannel / SSPI / token / user-profile interaction, not general network failure.
Repro
Inside sandbox:
whoami
Invoke-WebRequest -UseBasicParsing -Uri 'https://api.ipify.org'
curl.exe https://api.ipify.org
Observed:
whoami= real user- Schannel HTTPS fails with
SEC_E_NO_CREDENTIALS
Outside sandbox, the same PowerShell HTTPS call succeeds.
Expected behavior
HTTPS requests using Schannel should work in the Windows sandbox when network access is allowed.
Actual behavior
Schannel HTTPS fails in the current sandboxed shell with SEC_E_NO_CREDENTIALS.
Related issue discovered during investigation
The offline firewall setup also appears broken:
setup_error.json contains:
{
"code": "helper_firewall_rule_create_or_add_failed",
"message": "SetRemotePorts failed: Error { code: HRESULT(0x80070057), message: \"Параметр задан неверно.\" }"
}
That may be a separate Windows sandbox bug.
Suggested investigation areas
- Determine whether the active Windows shell sandbox path is using:
- dedicated sandbox users
- or a restricted-token-on-real-user path
- Compare the failing shell token/context with the successful
CodexSandboxOnline/CodexSandboxOfflinesessions. - Investigate what Schannel/SSPI state is missing in the restricted real-user sandbox context.
- Separately fix the incorrect env inheritance in the elevated sandbox path.
- Consider whether
#17458is a prerequisite or partial trigger for this issue, even if it is not the full root cause.
8 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Still an issue.
Additional reproduction: Schannel fails under
CodexSandboxOnlineI can reproduce this issue with the current Windows elevated sandbox.
Environment
26200.88940.146.0-alpha.3.1Sandbox identity
Unlike the restricted real-user context described in the original report, the current command process is running as the dedicated sandbox account:
Outside the sandbox, the same process runs as the normal Windows user with
CloudAPauthentication.USERPROFILEinside the sandbox still points to the normal user's profile directory rather than a dedicatedCodexSandboxOnlineprofile.Reproduction
Inside the sandbox:
Both fail before the TLS handshake:
This reproduces with:
Control tests
The same commands executed outside the sandbox succeed:
Node.js using OpenSSL succeeds inside the sandbox:
This rules out general DNS, TCP, firewall, certificate trust, and network access problems.
Additional checks:
CryptSvcandKeyIsoare runningerrors=[]Current conclusion
The failure is specific to Schannel/SSPI credential acquisition under the
CodexSandboxOnlinelogon context. The dedicated sandbox identity does not appear to have the per-user or logon state required for Schannel to acquire its default outbound credential handle.Because
network_access = true, standard Windows HTTPS clients should work inside the sandbox, or this limitation should be documented explicitly.Analysis: current elevated-sandbox path launches
CodexSandboxOnlinewithdwLogonFlags=0and no profile initialization anywhere — still present at current HEADIssue anchor: #17459, with the 2026-07-27 reproduction by @Revolt3738 on command runner
0.146.0-alpha.3.1(process identityCodexSandboxOnline,errors=[]from setup refresh, PowerShell 5.1 / 7.6.4 / Schannel curl all failing pre-handshake withSEC_E_NO_CREDENTIALS, Node/OpenSSL fine in the same sandbox).The current-source precondition for that reproduction is live (verified 2026-07-28 at
main=4c434651; paths below are undercodex-rs/):provision_sandbox_users→ensure_local_usercallsNetUserAdd(codex-rs/windows-sandbox-rs/src/bin/setup_main/win/sandbox_users.rs:70-115,NetUserAddat :115). Nothing in the setup path callsLoadUserProfile.dwLogonFlags=0.CreateProcessWithLogonW(..., /*dwlogonflags*/ 0, ...)with the in-code comment "Sandbox users have no profile state that commands should inherit" (codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs:346-352). Microsoft's documentation forCreateProcessWithLogonWstates the profile is not loaded by default and callers needing normal HKCU state must useLOGON_WITH_PROFILE(or load it explicitly). A repo-wide grep confirmsLoadUserProfile,LOGON_WITH_PROFILE, andCreateEnvironmentBlockappear nowhere inwindows-sandbox-rs/src.codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs:269-287) and spawns the child viaCreateProcessAsUserW(codex-rs/windows-sandbox-rs/src/conpty/mod.rs:146-158). The restricted token still usesDISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED(codex-rs/windows-sandbox-rs/src/token.rs:480).token_tests.rsasserts restricting-SID presence only; nothing exercises Schannel credential acquisition, profile loading, or HTTPS from inside the sandbox.Honest causal boundary. The bug and the current no-profile launch precondition are high-confidence; the exact SSPI causality is not proven:
SEC_E_NO_CREDENTIALSis a credential-acquisition failure, but that alone doesn't identify which missing profile/logon/token property causes it in each mode.AcquireCredentialsHandlecall lives in external child software (curl/PowerShell/Git), not in Codex itself; the repo contains no Schannel/SSPI handling at all.So this is not a claim that restoring
LOGON_WITH_PROFILEfixes everything. A definitive fix decision needs a Windows A/B matrix:dwLogonFlags=0baseline;LOGON_WITH_PROFILE;LoadUserProfile/CreateEnvironmentBlock;AcquireCredentialsHandle+ HTTPS behavior and inspectUSERPROFILE/HKCU in the child.For completeness: merged PR #27944 (stale-credential recovery) touches this same launch call but only handles
ERROR_LOGON_FAILUREretry — it doesn't change profile loading.Analysis only; no PR attached per contribution policy.
No unsolicited PR from me; happy to refine or draft fail-first tests if maintainers invite a fix direction.
Root-cause hypothesis: see mechanism chain above.
Additional reproduction: a nested Codex CLI loses both Windows TLS credentials and the host ChatGPT login
Reproduced on 2026-07-31 after restarting the app.
Environment
2620026.721.11231.00.146.0-alpha.3.1sandbox_mode = "workspace-write"[windows] sandbox = "elevated"sandbox_workspace_write.network_access = trueon-requestReproduction and controls
``
text
``<machine>\CodexSandboxOffline
``
text
``curl: (35) schannel: AcquireCredentialsHandle failed:
SEC_E_NO_CREDENTIALS (0x8009030e)
``
text
``codex login status
Not logged in
exit 1
``
text
``codex login status
Logged in using ChatGPT
exit 0
codex execfrom the Desktop sandbox fails on both WebSocket and HTTP fallback with:``
text
``native_root_error_count=1
invalid peer certificate: UnknownIssuer
CODEX_CA_CERTIFICATEto a valid PEM CA bundle changes the failure rather than fixing it: TLS reaches the service, then the nested CLI receives HTTP401 Missing bearer or basic authentication. This separates the certificate-trust problem from the missing-login problem.codex execthrough an explicitly approved host boundary succeeds. Two clean control runs returned exactly:``
text
``TLS_PROBE_OK
RESTART_HOST_OK
CodexSandboxOfflineTLS/login behavior.Impact
Codex Desktop cannot directly launch an authenticated child Codex CLI from its sandbox, even though the Desktop app and host CLI are already signed in. The safe current workaround is to approve a host-boundary launch and then keep the child CLI inside its own read-only/workspace-write sandbox.
Expected behavior / product question
Either:
codex execworkflows.Copying
auth.json, injecting long-lived tokens, or disabling TLS verification would weaken the security boundary and should not be necessary.Related: #21470 and #31073. Matching in-app feedback with logs was submitted. Feedback ID:
019fb996-7f0e-7421-9136-2083b18cf271.Analysis (community)
@EvilHumphrey — your nested Codex CLI case (app
26.721.11231.0, CLI0.146.0-alpha.3.1, elevated sandbox,network_access = true) is the same launch precondition I posted on #17459, plus a second symptom (host ChatGPT login missing inside the nested CLI).Root-cause hypothesis (re-checked 2026-08-20 at
main3b45c2906): the elevated runner still startsCodexSandboxOnlinewith no profile load. Microsoft TLS/Schannel then failsAcquireCredentialsHandle(SEC_E_NO_CREDENTIALS). A nested CLI in that identity can also fail to see the host login store for the same reason: HKCU/profile/logon state was never initialized.Still live:
CreateProcessWithLogonW(..., /*dwlogonflags*/ 0, ...)with the comment that sandbox users have no profile state (codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs:348-352).NetUserAdds the dedicated account withoutLoadUserProfile(sandbox_users.rsprovision path).LOGON_WITH_PROFILE/LoadUserProfilestill absent underwindows-sandbox-rs/src.Honest boundary (unchanged): this does not prove that
LOGON_WITH_PROFILEalone fixes every mode. #17458 already showed profile directories can exist and Schannel can still fail. Nested CLI losing ChatGPT login is consistent with missing HKCU, but I have not A/B'd that on Windows here.No unsolicited PR. Useful A/B if anyone has a Windows box: current
dwLogonFlags=0vsLOGON_WITH_PROFILEvs explicitLoadUserProfile, asserting both Schannel HTTPS and whether~/.codex/host login files are visible to the nested CLI.Additional current reproduction: both elevated and unelevated restricted tokens fail Schannel
I independently reproduced this issue on 2026-08-20 with the current native Windows sandbox.
Environment
2620026.814.5517.00.148.0-alpha.15sandbox_mode = "workspace-write"sandbox_workspace_write.network_access = true[windows] sandbox = "elevated"[windows] sandbox = "unelevated"Reproduction matrix
| Execution context | Windows token | PowerShell / Schannel curl | Node.js HTTPS |
|---|---|---|---|
| Normal host process |
IsTokenRestricted=False| succeeds (HTTP 200) | succeeds || Elevated Codex sandbox (
CodexSandboxOnline) |IsTokenRestricted=True| fails withSEC_E_NO_CREDENTIALS| succeeds (HTTP 200) || Unelevated Codex sandbox (normal user identity) |
IsTokenRestricted=True| fails withSEC_E_NO_CREDENTIALS| succeeds (HTTP 200) |The failing commands include:
The curl failure is:
A Node.js HTTPS request to the same destination succeeds from the same sandboxed process context.
Additional controls:
curl.execommands succeed outside the sandbox.errors=[]).Diagnostic implication
This reproduction follows the restricted sandbox token in both native sandbox implementations:
CodexSandboxOnlineidentity.That makes missing dedicated-user profile state a plausible contributing factor for elevated mode, but not a complete explanation by itself. The shared condition across both failing modes is the restricted token, while the success of Node/OpenSSL rules out a general DNS, TCP, firewall, or certificate-trust failure.
A useful next A/B would be to vary the restricted-token flags and restricting SID set one at a time, while separately testing profile loading (
LOGON_WITH_PROFILE/LoadUserProfile) and asserting SchannelAcquireCredentialsHandlebehavior.No credentials, private repository names, or user-specific paths are included in this report.
Analysis (community)
@braze-taffo — your 2026-08-20 matrix on #17459 (Desktop
26.814.5517.0, CLI0.148.0-alpha.15) is the first public split I have seen that unconfounds missing profile from restricted-token. Both elevatedCodexSandboxOnlineand unelevated (real-user identity) fail Schannel withSEC_E_NO_CREDENTIALSwhileIsTokenRestricted=True; the unrestricted host process succeeds; Node.js HTTPS in the same sandboxed process succeeds. That is a different residual than the nested-CLI / no-profile note I posted earlier.Root-cause hypothesis (re-checked 2026-08-21 at
main536f86e5c): Schannel/AcquireCredentialsHandleis breaking on the restricted token, not only on a missingCodexSandboxOnlineprofile. Unelevated never callsCreateProcessWithLogonW. It takes the interactive user’s token, runsCreateRestrictedTokenwithDISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED, thenCreateProcessAsUserW. The child is still the signed-in user (so HKCU/profile can exist) and stillIsTokenRestricted=True. That matches your unelevated row. Node/OpenSSL never talk to Schannel, so they stay green in every row.Live path (paths under
codex-rs/):CreateRestrictedTokenflags areDISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED(windows-sandbox-rs/src/token.rs:42-44, 480-483). The restricting SID list is capabilities + logon + Everyone (:461-477). Spawn isCreateProcessAsUserW(windows-sandbox-rs/src/process.rs:153-165), notCreateProcessWithLogonW. Repo grep:LOGON_WITH_PROFILE/LoadUserProfilestill absent underwindows-sandbox-rs/src.CreateProcessWithLogonW(..., /*dwlogonflags*/ 0, ...)with the comment that sandbox users have no profile state (windows-sandbox-rs/src/elevated/runner_client.rs:346-352). After that logon the command runner still derives a restricted token with the same three flags. So elevated is no profile plus restricted token; unelevated is real profile plus restricted token. Your matrix says both fail Schannel, which is why I no longer treatdwLogonFlags=0as the only live precondition.WRITE_RESTRICTEDplus extra restricting SIDs is the most plausible Schannel/CAPI break, but #17458 already showed “profile directory exists” is not sufficient. Node succeeding is TLS-stack evidence, not a proof thatnetwork_access = trueis a no-op (the capability SID is still attached).No unsolicited PR. Useful next A/B if anyone has a Windows host: same
workspace-write+network_access = truecommand under (a) current restricted token, (b) restricted token withoutWRITE_RESTRICTED, (c) unrestrictedCreateProcessAsUserWof the same user, assertingInvoke-WebRequest/curl.exeSchannel vs Node HTTPS. ElevateddwLogonFlags=0vsLOGON_WITH_PROFILEremains a second axis, not a replacement for (a)–(c).