Summary
codex-multi-auth-codex app-server takes the shadow-CODEX_HOME transport, and a resident server cannot live there. On any machine that has previously run an app-server it does not start at all; where it does start, every attached client drives threads against a frozen copy of the thread index.
This is the same class of defect as #647, which was fixed for resume/fork in #648 by routing them to the canonical-home app helper. app-server was left on the shadow path.
Environment
codex-cli 0.147.0
codex-multi-auth 2.8.3
node v24.16.0
macOS 26.5.2
Account-pool output from codex-multi-auth status / report --json is omitted deliberately — it carries account emails, and nothing in this report depends on it. Reproduces with any single managed account and runtime rotation on (the default).
Reproduction 1 — the server refuses to start
Codex requires <CODEX_HOME>/app-server-control to be a directory, and the check is lstat-strict. Codex itself creates that directory the first time an app-server runs, so most real homes have one. The shadow mirror links directories rather than copying them, so the shadow home gets a symlink at that path.
$ ls -ld ~/.codex/app-server-control
drwx------ 3 user staff 96 ... /Users/user/.codex/app-server-control
$ codex-multi-auth-codex --account <id> app-server --listen unix:///path/to/a.sock
Error: socket directory path exists and is not a directory: \
/Users/user/.codex/multi-auth/runtime-shadow-homes/codex-multi-auth-runtime-home-XXXXXX/app-server-control
Exit 1, no server. Removing ~/.codex/app-server-control makes it start again, which confirms the mirror's symlink is the trigger.
Unrelated but easy to trip over while testing: Codex applies the same lstat-strict check to the listen socket's parent directory, so --listen unix:///tmp/x.sock fails on macOS because /tmp is a symlink to /private/tmp. Use real paths.
Reproduction 2 — a server that does start serves a frozen thread index
The runtime-rotation shadow mirror snapshots state_N.sqlite rather than linking it. For resume that was #647: the requested thread was absent and the TUI hung. For an app-server the same divergence is held for the life of the process and handed to every client that attaches, and any thread the server creates is written into a copy that is discarded on exit.
$ ls -l ~/.codex/state_5.sqlite
-rw-r--r-- 1 user staff 6799360 ... /Users/user/.codex/state_5.sqlite
$ ls -l <shadow home>/state_5.sqlite # while the server runs
-rw-r--r-- 1 user staff 6799360 ... # a copy, not a link; discarded at exit
Expected
app-server runs against the canonical CODEX_HOME, like the interactive TUI and resume/fork already do, receiving the rotation provider through -c overrides.
Why it matters
An app-server is the programmatic entry point: clients attach to it and drive whole sessions through it, and the account bound to the server process is the one those sessions bill. Runtime rotation is on by default, so on 0.147.0 the wrapper's app-server path is effectively unusable.
Fix
Happy to send the patch — it is one predicate plus two details, and it follows #648's shape. PR to follow.
Summary
codex-multi-auth-codex app-servertakes the shadow-CODEX_HOMEtransport, and a resident server cannot live there. On any machine that has previously run an app-server it does not start at all; where it does start, every attached client drives threads against a frozen copy of the thread index.This is the same class of defect as #647, which was fixed for
resume/forkin #648 by routing them to the canonical-home app helper.app-serverwas left on the shadow path.Environment
Account-pool output from
codex-multi-auth status/report --jsonis omitted deliberately — it carries account emails, and nothing in this report depends on it. Reproduces with any single managed account and runtime rotation on (the default).Reproduction 1 — the server refuses to start
Codex requires
<CODEX_HOME>/app-server-controlto be a directory, and the check islstat-strict. Codex itself creates that directory the first time an app-server runs, so most real homes have one. The shadow mirror links directories rather than copying them, so the shadow home gets a symlink at that path.Exit 1, no server. Removing
~/.codex/app-server-controlmakes it start again, which confirms the mirror's symlink is the trigger.Unrelated but easy to trip over while testing: Codex applies the same
lstat-strict check to the listen socket's parent directory, so--listen unix:///tmp/x.sockfails on macOS because/tmpis a symlink to/private/tmp. Use real paths.Reproduction 2 — a server that does start serves a frozen thread index
The runtime-rotation shadow mirror snapshots
state_N.sqliterather than linking it. Forresumethat was #647: the requested thread was absent and the TUI hung. For an app-server the same divergence is held for the life of the process and handed to every client that attaches, and any thread the server creates is written into a copy that is discarded on exit.Expected
app-serverruns against the canonicalCODEX_HOME, like the interactive TUI andresume/forkalready do, receiving the rotation provider through-coverrides.Why it matters
An app-server is the programmatic entry point: clients attach to it and drive whole sessions through it, and the account bound to the server process is the one those sessions bill. Runtime rotation is on by default, so on 0.147.0 the wrapper's app-server path is effectively unusable.
Fix
Happy to send the patch — it is one predicate plus two details, and it follows #648's shape. PR to follow.