Skip to content

Windows 0.159.2: daemon install os error 5 is at std::fs::rename(stage, release), not the junction in #48853 #49449

Description

@Water-Run

Summary

On codex-cli 0.159.2 (Windows 11 x64, build 26220) the managed app-server daemon install fails with a bare os error 5, and evidence points at a different failing call than the one identified in #48853. Here, the junction conversion in select_release succeeds; the install dies earlier, at std::fs::rename(stage, release). Flagging this because the fix proposed in #48853 (fall back when FSCTL_SET_REPARSE_POINT is denied) would never be reached on this machine.

Symptom:

Installing daemon from CLI version 0.159.2 into C:\Users\linzh\.codex\packages\app-server-daemon...
Error: 拒绝访问。 (os error 5)

and from the TUI:

Error: 拒绝访问。 (os error 5)
To work without the background server, rerun the same command with --no-daemon (including resume or fork and its arguments).

(The localized text is just this machine's locale; it is the same ERROR_ACCESS_DENIED.)

The staged package is complete, but releases/ stays empty

Polling packages\app-server-daemon\releases every 60 ms while codex app-server daemon start runs, the last snapshot before the process exits is:

last staging snapshot    : 45 files / 427.6MB
releases/                : (empty)

45 files / 427.6 MiB is the entire vendor tree (bin\codex.exe 309.7 MiB, bin\codex-code-mode-host.exe 71.2 MiB, codex-path\rg.exe, codex-resources\..., codex-package.json). So in prepare_install.rs everything up to and including validate_package(stage) succeeds, and package_tree writes every file.

The staging directory is a tempfile::TempDir and is removed on drop. If std::fs::rename(stage.path(), &release) had succeeded, releases\0.159.2-x86_64-pc-windows-msvc would still be there afterwards. It is not — releases/ is empty apart from unrelated leftovers.

The junction path works fine here

I pre-staged a valid release directory so that release.try_exists()? at prepare_install.rs:254 is true, which makes the installer take the warm path and skip the rename entirely:

$vendor = "<npm global>\@openai\codex\node_modules\@openai\codex-win32-x64\vendor\x86_64-pc-windows-msvc"
$rel = "$env:USERPROFILE\.codex\packages\app-server-daemon\releases\0.159.2-x86_64-pc-windows-msvc"
New-Item -ItemType Directory -Force -Path $rel
Copy-Item (Join-Path $vendor '*') $rel -Recurse -Force   # full 45-file tree
codex app-server daemon start
{"status":"started","backend":"pid","pid":42808,
 "managedCodexPath":"C:\\Users\\linzh\\.codex\\packages\\app-server-daemon\\current\\bin\\codex.exe",
 "managedCodexVersion":"0.159.2","cliVersion":"0.159.2","appServerVersion":"0.159.2"}

current did not exist beforehand, so select_release took its cold path — temp dir, create_dir(current), retarget_junction (DeviceIoControl(FSCTL_SET_REPARSE_POINT)), rename — and the result is a working junction:

Get-Item "$env:USERPROFILE\.codex\packages\app-server-daemon\current" -Force
# Mode  Name      LinkType
# l---- current   Junction   ->  ...\releases\0.159.2-x86_64-pc-windows-msvc

auto-update-version was written at the same timestamp. So on this machine FSCTL_SET_REPARSE_POINT succeeds and the daemon starts and runs normally. The failing call is upstream of it.

Where I think it actually fails

// codex-rs/app-server-daemon/src/prepare_install.rs  (tag rust-v0.159.2)
// 229  let digest = package_tree(source, Some(stage.path()))?;   // OK - all 45 files written
// 230  validate_package(stage.path())?;                          // OK
// 236  anyhow::ensure!(package_tree(source, None)? == digest      // OK
//          && std::fs::read(stage.path().join("codex-package.json"))? == manifest_bytes
//          && managed_install::executable_identity(&staged_exe).await? == running_identity, ...)
// 254  if release.try_exists()? { ... } else {
// 265      std::fs::rename(stage.path(), &release)?;              // <-- ERROR_ACCESS_DENIED

The error is a bare Error: 拒绝访问。 (os error 5) with no Caused by: chain, ~2 s after the Installing daemon from CLI version 0.159.2 into ... line. That detail matters, because the neighbouring candidates are distinguishable by whether they carry context:

  • install_lock.rs treats PermissionDenied as "lock busy" and retries for 30 s, then fails with timed out waiting for daemon installer lock ... — not what we see, so the install lock is fine.
  • executable_identity() wraps errors in failed to read executable ...; managed_codex_version() wraps them in failed to invoke managed Codex binary .... Neither text appears, and no codex.exe --version child process is ever spawned, so execution stops before both.
  • package_tree(root, None) and std::fs::read(stage/codex-package.json) are the only bare ? left in that block. A faithful Node replication of package_tree(root, None) — same DFS order (Vec stack, reverse-sorted readdir), realpath + lstat + full read of every file — completes over all 45 files with no error. So the source-side re-read does not look like the culprit.

That leaves std::fs::rename(stage, release) at line 265. MoveFileEx on a directory fails with ERROR_ACCESS_DENIED when a file inside the tree is open without FILE_SHARE_DELETE, and this tree just received a 309.7 MiB codex.exe and a 71.2 MiB codex-code-mode-host.exe. Something holding either briefly after the write — a scanner, or a codex handle — would produce exactly this. This is a hypothesis I could not prove directly; I was not able to capture the failing CreateFile/MoveFileEx.

If this holds, a MoveFileEx retry (or staging outside the same volume / renaming a bare directory before populating it) would fix the case that #48853's junction fallback does not.

Eliminations, for the record

Hypothesis Evidence against
ACLs on .codex CODEX_HOME pointed at a brand-new C:\codexhome and at a brand-new %TEMP% dir: identical failure. Zero DENY ACEs anywhere in the vendor tree. The several hundred S-1-5-21-<other-machine>-... DENY ACEs on .codex come from cloned profiles, and none of those SIDs are in the user token (whoami /groups lists only WaterRun\docker-users).
Controlled Folder Access Get-MpPreference.EnableControlledFolderAccess = 0 (disabled).
Antivirus Real-time protection is enabled here (unlike the report in #48853), and the Defender Operational log has no matching events since 09:20.
One specific file in the package The real 8.2 MiB codex-command-runner.exe alone, and the real 17.7 MiB codex-windows-sandbox-setup.exe alone, both stage successfully. Shrinking both to 1 MiB dummies still fails. Removing all of codex-resources changes the error to local Codex package is missing codex-resources/codex-command-runner.exe, proving package_tree completes.
Working directory Reproduced from D:\工作\项目\yy-subsystems\systems\infra\yy-agent-hub, from %TEMP%, and with a fresh home on C:\.

Impact and verification

  • codex exec is unaffected — it does not need the daemon. With the real ~/.codex it completed and returned PONG (exit 0, 35.7 s, model gpt-6.1-sol).
  • With the release pre-staged, codex --yolo launched in the project directory stayed alive (20 s, no exit) and created a new session rollout (~10 MB). Before the workaround it exited in under a second with os error 5.
  • codex app-server daemon version then reports {"status":"running",...,"appServerVersion":"0.159.2"}.

This also independently confirms @shgef's correction in #48853: the release directory has to mirror the whole vendor tree, not just bin\codex.exe.

Reproduction

codex app-server daemon start   # -> Error: 拒绝访问。 (os error 5), exit 1, ~2s

codex app-server daemon start reproduces without a tty, so it is easier to automate than the TUI path.

Relationship to other reports

Environment

  • Windows 11 Pro x64, 10.0.26220
  • codex-cli 0.159.2, npm global install
  • Non-elevated PowerShell
  • Defender real-time protection on, Controlled Folder Access off
  • HTTP_PROXY/HTTPS_PROXY = http://127.0.0.1:7890

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions