[Windows standalone] Installer PATH junction cannot be traversed in OpenSSH sessions (0x800701C0)
<!-- Proposed title:
[Windows standalone] Installer PATH junction cannot be traversed in OpenSSH sessions (0x800701C0)
-->
What version of Codex CLI is running?
codex-cli 0.149.1
What subscription do you have?
ChatGPT Pro Lite
Which model were you using?
Not applicable. The failure occurs while Windows resolves/opens codex.exe, before the CLI starts or selects a model.
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
Microsoft Windows 11 Pro [Version 10.0.26200.8875]
OpenSSH_for_Windows_9.5p2, LibreSSL 3.8.2
sshd.exe 9.5.5.1
What terminal emulator and version are you using (if applicable)?
- Client: macOS Terminal on Apple Silicon, connecting with
ssh <user>@<windows-host> - Remote default shell: Windows PowerShell (also reproduced from
cmd.exe) - MSYS2 Bash:
5.2.37(2)-release; MSYS runtime3.6.5
Codex doctor report
The report below was captured after reproducing the problem by invoking the physical release binary directly, with the temporary workaround directory removed from PATH. Username-bearing paths were redacted. It was captured non-interactively, so the TERM=dumb result is an artifact of the diagnostic capture and is unrelated to the SSH junction failure.
<details>
<summary>Full reviewed <code>codex doctor --json</code> output</summary>
{
"schemaVersion": 1,
"generatedAt": "1787728043s since unix epoch",
"overallStatus": "fail",
"codexVersion": "0.149.1",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "C:\\Users\\<user>\\.codex\\app-server-control\\app-server-control.sock",
"daemon state dir": "C:\\Users\\<user>\\.codex\\app-server-daemon",
"mode": "ephemeral",
"pid file": "C:\\Users\\<user>\\.codex\\app-server-daemon\\app-server.pid (missing)",
"settings": "C:\\Users\\<user>\\.codex\\app-server-daemon\\settings.json (missing)",
"status": "not running",
"update-loop pid file": "C:\\Users\\<user>\\.codex\\app-server-daemon\\app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "C:\\Users\\<user>\\.codex\\auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "C:\\Users\\<user>\\.codex",
"config.toml": "C:\\Users\\<user>\\.codex\\config.toml",
"config.toml parse": "ok",
"cwd": "C:\\Users\\<user>",
"enabled feature flags": "<redacted>",
"feature flag overrides": "none",
"feature flags enabled": "43",
"log dir": "C:\\Users\\<user>\\.codex\\log",
"mcp servers": "1",
"model": "gpt-5.6-sol",
"model provider": "openai",
"sqlite home": "C:\\Users\\<user>\\.codex"
},
"remediation": null,
"durationMs": 0
},
"desktop.app.version": {
"id": "desktop.app.version",
"category": "desktop",
"status": "ok",
"summary": "the desktop application is installed",
"details": {
"log directory": "$HOME\\AppData\\Local\\Codex/Logs",
"running": "true",
"version": "26.820.7780.0"
},
"remediation": null,
"durationMs": 0
},
"desktop.app_server.handshake": {
"id": "desktop.app_server.handshake",
"category": "desktop",
"status": "ok",
"summary": "the desktop app-server initialized successfully",
"details": {},
"remediation": null,
"durationMs": 0
},
"desktop.security.enforcement": {
"id": "desktop.security.enforcement",
"category": "desktop",
"status": "ok",
"summary": "no locally visible recent Codex security enforcement was found",
"details": {},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.51.2.windows.1",
"details": {
"PATH git #1": "C:\\Program Files\\Git\\cmd\\git.exe",
"PATH git #2": "C:\\Users\\<user>\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\native\\git\\cmd\\git.exe",
"PATH git entries": "2",
"git build options": "git version 2.51.2.windows.1; cpu: x86_64; built from commit: bd2ecbae58213046a468256b95fc4864de25bdf5; sizeof-long: 4; sizeof-size_t: 8; shell-path: D:/git-sdk-64-build-installers/usr/bin/sh; feature: fsmonitor--daemon; libcurl: 8.16.0; OpenSSL: OpenSSL 3.5.4 30 Sep 2025; zlib: 1.3.1; SHA-1: SHA1_DC; SHA-256: SHA256_BLK; default-ref-format: files; default-hash: sha1",
"git exec path": "C:/Program Files/Git/mingw64/libexec/git-core",
"git version": "git version 2.51.2.windows.1",
"repo detected": "false",
"selected git": "C:\\Program Files\\Git\\cmd\\git.exe"
},
"remediation": null,
"durationMs": 109
},
"git.worktree.dev_drive": {
"id": "git.worktree.dev_drive",
"category": "git",
"status": "ok",
"summary": "no Git worktree is active",
"details": {},
"remediation": null,
"durationMs": 0
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "C:\\Users\\<user>\\AppData\\Local\\Programs\\OpenAI\\Codex\\bin\\codex.exe",
"PATH codex #2": "C:\\Users\\<user>\\AppData\\Local\\OpenAI\\Codex\\bin\\d0097be4feba73d0\\codex.exe",
"PATH codex entries": "2",
"current executable": "C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\bin\\codex.exe",
"install context": "standalone (windows, package C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc, bin C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\bin, resources C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\codex-resources, path C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\codex-path)",
"managed by bun": "false",
"managed by npm": "false",
"managed by pnpm": "false",
"managed package root": "not set"
},
"remediation": null,
"durationMs": 105
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "1",
"disabled servers": "0",
"stdio servers": "1"
},
"remediation": null,
"durationMs": 0
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"managed proxy": "not configured",
"proxy env vars": "none",
"respect system proxy": "disabled"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT inference URL": "https://chatgpt.com/backend-api/<redacted> reachable (HTTP 405)",
"desktop assets CDN": "https://persistent.oaistatic.com/codex-app-prod/<redacted> reachable (HTTP 200)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 205
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "2 IPv4, 2 IPv6, first IPv6",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 703
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on windows-x86_64",
"details": {
"commit": "unknown",
"current executable": "C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\bin\\codex.exe",
"install method": "standalone (windows, package C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc, bin C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\bin, resources C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\codex-resources, path C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\codex-path)",
"platform": "windows-x86_64",
"version": "0.149.1"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "C:\\Users\\<user>\\.codex\\packages\\standalone\\releases\\0.149.1-x86_64-pc-windows-msvc\\codex-path\\rg.exe",
"search command readiness": "file exists",
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "Never",
"codex-linux-sandbox helper": "none",
"denied-read restrictions": "false",
"execve wrapper helper": "none",
"filesystem sandbox": "unrestricted",
"network sandbox": "enabled",
"sandbox backend": "<redacted>"
},
"remediation": null,
"durationMs": 0
},
"security.endpoint": {
"id": "security.endpoint",
"category": "security",
"status": "warning",
"summary": "endpoint protection detected; Codex exclusions are unverified",
"details": {
"Codex exclusions": "not verified",
"endpoint products": "Microsoft Defender",
"exclusion targets": "signed Codex app; codex.exe; codex-windows-sandbox-setup.exe; codex-command-runner.exe; codex-code-mode-host.exe"
},
"issues": [
{
"severity": "warning",
"cause": "Microsoft Defender can interfere with Codex. Verify Codex exclusions.",
"measured": "not verified",
"expected": "Codex application and helper exclusions",
"remedy": "Microsoft Defender: Add a certificate or executable-path exclusion for Codex and its helpers. If Attack Surface Reduction blocks Codex, add a rule exclusion. If Controlled Folder Access blocks Codex, allow the app.",
"fields": ["Codex exclusions"]
}
],
"remediation": "ask your security administrator to verify Codex exclusions and required helper allowances",
"durationMs": 30
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "C:\\Users\\<user>\\.codex (dir)",
"active rollout files": "2906 files, 1973464467 total bytes, 679099 average bytes",
"archived rollout files": "2 files, 852345 total bytes, 426172 average bytes",
"goals DB": "C:\\Users\\<user>\\.codex\\goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "C:\\Users\\<user>\\.codex\\logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "C:\\Users\\<user>\\.codex\\log (dir)",
"memories DB": "C:\\Users\\<user>\\.codex\\memories_1.sqlite (file)",
"memories DB integrity": "ok",
"queue DB": "C:\\Users\\<user>\\.codex\\queue_1.sqlite (file)",
"queue DB integrity": "ok",
"sqlite home": "C:\\Users\\<user>\\.codex (dir)",
"standalone release cache": "1 entries in C:\\Users\\<user>\\.codex\\packages\\standalone\\releases",
"state DB": "C:\\Users\\<user>\\.codex\\state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "C:\\Users\\<user>\\.codex\\thread_history_1.sqlite (file)",
"thread history DB integrity": "ok"
},
"remediation": null,
"durationMs": 1537
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "ok",
"summary": "rollout files and state DB thread inventory agree",
"details": {
"default model provider": "openai",
"rollout DB active files": "2906",
"rollout DB active rows": "2906",
"rollout DB archive mismatches": "0",
"rollout DB archived files": "2",
"rollout DB archived rows": "2",
"rollout DB duplicate DB paths": "0",
"rollout DB duplicate rollout thread ids": "0",
"rollout DB malformed file names": "0",
"rollout DB missing active rows": "0",
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=2908",
"rollout DB rows": "2908",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "exec=2173, cli=707, vscode=16, subagent:other=11, subagent:thread_spawn=1",
"rollout DB stale rows": "0"
},
"remediation": null,
"durationMs": 1523
},
"system.disk": {
"id": "system.disk",
"category": "disk",
"status": "ok",
"summary": "sufficient free disk space (1117.8 GiB)",
"details": {
"CODEX_HOME available": "1117.8 GiB",
"failure threshold": "1.0 GiB",
"warning threshold": "5.0 GiB",
"worktree available": "1117.8 GiB"
},
"remediation": null,
"durationMs": 0
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language en-US",
"details": {
"EDITOR": "not set",
"GH_PAGER": "set",
"GIT_PAGER": "set",
"LANG": "C.UTF-8",
"LC_ALL": "C.UTF-8",
"LC_CTYPE": "C.UTF-8",
"PAGER": "set",
"VISUAL": "not set",
"os": "Windows 10.0.26200 (Windows 11 Professional) [64-bit]",
"os language": "en-US",
"os type": "Windows",
"os version": "10.0.26200"
},
"remediation": null,
"durationMs": 0
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "fail",
"summary": "TERM=dumb - colors and cursor control are disabled",
"details": {
"COLORTERM": "present",
"NO_COLOR": "1",
"TERM": "dumb",
"color output": "disabled (NO_COLOR)",
"console input code page": "437",
"console output code page": "65001",
"effective locale": "C.UTF-8",
"stderr console mode": "unavailable",
"stderr is terminal": "false",
"stdin is terminal": "false",
"stdout console mode": "unavailable",
"stdout is terminal": "false",
"terminal": "dumb",
"terminal size": "120x30"
},
"issues": [
{
"severity": "fail",
"cause": "TERM=dumb - colors and cursor control are disabled",
"measured": "TERM=dumb",
"expected": "TERM=xterm-256color or another real terminal type",
"remedy": "set TERM to a real value, for example xterm-256color",
"fields": ["TERM"]
}
],
"remediation": null,
"durationMs": 0
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"terminal title activity": "true",
"terminal title items": "activity, project-name",
"terminal title project source": "cwd",
"terminal title project value": "<user>",
"terminal title source": "default"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "ok",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.149.1",
"check for update on startup": "true",
"dismissed version": "0.149.0",
"last checked at": "2026-08-25T19:08:10.716378Z",
"latest version": "0.149.1",
"latest version status": "current version is not older",
"update action": "standalone installer",
"version cache": "C:\\Users\\<user>\\.codex\\version.json"
},
"remediation": null,
"durationMs": 258
}
}
}
</details>
What issue are you seeing?
The Windows standalone installer puts this stable directory on PATH:
%LOCALAPPDATA%\Programs\OpenAI\Codex\bin
Locally, codex --version works. In a Windows OpenSSH login using the same Windows account, PATH contains that directory, but Windows refuses to traverse the installer's junction chain. The failure therefore looks like a missing command at first, even though the system PATH is correct.
Opening the advertised executable from the SSH session produces:
System.IO.IOException
HRESULT: 0x800701C0
Win32 error: 448 (ERROR_UNTRUSTED_MOUNT_POINT)
The path cannot be traversed because it contains an untrusted mount point.
The installer-created reparse chain is:
%LOCALAPPDATA%\Programs\OpenAI\Codex\bin
Junction -> %USERPROFILE%\.codex\packages\standalone\current\bin
%USERPROFILE%\.codex\packages\standalone\current
Junction -> %USERPROFILE%\.codex\packages\standalone\releases\0.149.1-x86_64-pc-windows-msvc
Both junctions are owned by the same Windows user that is logging in over SSH. The physical release executable works in the very same SSH process:
$release = (Get-Item -LiteralPath "$env:USERPROFILE\.codex\packages\standalone\current" -Force).Target
& "$release\bin\codex.exe" --version
# codex-cli 0.149.1
So the binary itself, permissions, authentication, and the SSH environment are functional; traversing the public installer junction is the failing operation.
What steps can reproduce the bug?
- Install the current standalone Codex CLI on Windows using the official PowerShell installer.
- Confirm locally that
codex --versionworks. - Enable Windows OpenSSH Server and SSH into the machine as the same user from another computer.
- Confirm that
%LOCALAPPDATA%\Programs\OpenAI\Codex\binis present in the remote process'sPATH. - In the remote PowerShell session, run:
``powershell``
codex --version
Get-Item -LiteralPath "$env:LOCALAPPDATA\Programs\OpenAI\Codex\bin\codex.exe"
[IO.File]::OpenRead("$env:LOCALAPPDATA\Programs\OpenAI\Codex\bin\codex.exe")
- Observe that command discovery/opening through the installer path fails, with the final operation reporting
0x800701C0/ERROR_UNTRUSTED_MOUNT_POINT. - Resolve the
currentjunction's target metadata and invoke the physical release path as shown above; it succeeds. - The same installer path is also unusable from MSYS2 Bash launched inside that SSH session.
What is the expected behavior?
The stable command path created and added to PATH by the standalone installer should be executable from Windows OpenSSH sessions for the installing user, including PowerShell, cmd.exe, and MSYS2 Bash.
Ideally, a fix would preserve the standalone layout's useful properties from #17022: atomic upgrades, bundled resources, correct standalone install detection, and concurrent running Codex instances. Falling back to npm or copying the current codex.exe into a trusted directory would lose some of those properties or become stale after an update.
Additional information
Confirmed update-safe workaround
I placed two regular launcher files in %USERPROFILE%\.local\bin, ahead of the installer junction on PATH:
codex.cmd: a Windows command shim for PowerShell andcmd.exe.codex: an executable, extensionless Bash wrapper (or Bash launcher shim) for MSYS2. MSYS Bash does not discover/invoke the.cmdshim ascodexin this setup.
Each launcher reads the standalone\current junction's Target metadata without executing through the junction, converts it to the physical release path, and then executes <release>\bin\codex.exe with all arguments. The .cmd shim uses the absolute inbox PowerShell path so it does not depend on MSYS's reduced Windows PATH.
The key operation is equivalent to:
$release = (Get-Item -LiteralPath "$env:USERPROFILE\.codex\packages\standalone\current" -Force).Target
& "$release\bin\codex.exe" @args
This has now been verified in local and SSH sessions from PowerShell, cmd.exe, and MSYS2 Bash. codex --version returns codex-cli 0.149.1, and the user independently confirmed the MSYS2 SSH case.
This workaround is durable across standalone updates because every new process resolves the current target at launch time. It also preserves concurrent instances: an already-running process continues using its loaded release while new invocations follow the atomically retargeted current junction. A raw copied codex.exe would instead become stale and could lose the physical standalone package context used for bundled resources and updating.
A product-level version could be a small regular codex.exe bootstrap/launcher in %LOCALAPPDATA%\Programs\OpenAI\Codex\bin that resolves standalone\current to the physical release and starts that binary, rather than exposing the release through a traversed directory-junction chain.
Related issues and installer design
I searched the existing issue tracker for 0x800701C0, ERROR_UNTRUSTED_MOUNT_POINT, untrusted mount point with OpenSSH, and Windows standalone junction/SSH combinations. I did not find an existing report for this exact failure.
- #17022 introduced the standalone release/current layout and explains why preserving atomic upgrades and physical package context is valuable.
- #32655 reports sandbox helpers being resolved relative to the Windows standalone PATH link rather than the physical package release. It involves the same layout but occurs after the CLI starts; this report is about Windows OpenSSH refusing to traverse the public launcher path at all.
- #30829 reports the sandbox setup helper not being found through the standalone
binjunction. Again, it is the same layout but a different helper-resolution failure. - #22757 covers Codex Desktop Remote SSH sending a POSIX bootstrap to a Windows PowerShell default shell. This report concerns manually logging into a working Windows OpenSSH session and launching the CLI.
- #35215 and #39449 mention
0x800701C0/ an “untrusted mount point” in Desktop/MSIX launch scenarios. Their reproduction paths and components differ from this standalone CLI/OpenSSH junction case.
I like the junction-based standalone update design and would prefer to keep it; the compatibility gap appears to be specifically the stable Windows command entrypoint crossing that reparse chain in an OpenSSH-created session.
2 Comments
Your 0x800701C0 is Windows refusing to trust the junction chain under sshd's filtered token, the reparse-point trust check treats the installer's
currentlink as an untrusted mount point even though it's your own profile. The physical release path working in the same session kind of proves it's the traversal, not the binary. Your shim is the solid workaround, resolve the target withGet-Item -LiteralPath ... -Forceand call the release exe directly. A regular launcher instead of the junction would dodge the trust path entirely, that's the fix that keeps #17022's atomic upgrade design intact.Agent:
@MilkyWay008 Thanks — that matches the important distinction shown by the control test: the physical release executable works in the same SSH session, so the failure is in traversing the installer-created junction chain rather than in
codex.exeitself.I agree that a regular native launcher which resolves
standalone\currentand then starts the physical release would be the cleanest fix. It would avoid this trust-path failure while preserving #17022's atomic updates, bundled package context, and concurrent running instances.The one detail I have not independently proven yet is that sshd's filtered token is specifically what triggers the trust decision. What I directly confirmed is that the SSH-created session returns
ERROR_UNTRUSTED_MOUNT_POINT/0x800701C0when crossing this junction chain, while the resolved physical path succeeds.Human:
Yea I like that the standalone installer allows atomic updates and would rather not go back to the npm install flow. But codex being available over SSH is crucial to me. In fact the first thing I do when SSHing into a Windows machine nowadays is run codex. The current junction set up breaks that workflow. I am interested in hearing more about this regular launcher you speak of. Any details?