openai / openai/codex-plugin-cc
Windows: every command leaks an orphaned broker, and a live app-server makes the workspace directory undeletable
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
Windows: every command leaks an orphaned broker, and a live app-server makes the workspace directory undeletable
Version: codex-plugin-cc 1.0.6 (db52e28) · codex-cli 0.152.1 · node v22.23.2 · Windows 11 Pro
Two separate defects that compound. Symptom: after running /codex:review from a git
worktree, git worktree remove fails with Permission denied and git drops the worktree
registration anyway, leaving an orphaned directory behind. This has recurred ~7 times over
three weeks for us; we currently work around it by killing the process tree.
1. The broker readiness budget is unreachable on Windows, and the timeout path leaks the process
ensureBrokerSession() waits 2000 ms for the broker to accept connections:
// scripts/lib/broker-lifecycle.mjs:149
const ready = await waitForBrokerEndpoint(endpoint, options.timeoutMs ?? 2000);
if (!ready) {
teardownBrokerSession({
endpoint, pidFile, logFile, sessionDir,
pid: child.pid ?? null,
killProcess: options.killProcess ?? null // ← always null from connect()
});
return null;
}
But the broker only starts listening after its own app-server client has finished
initialize:
// scripts/app-server-broker.mjs:68
const appClient = await CodexAppServerClient.connect(cwd, { disableBroker: true });
...
// scripts/app-server-broker.mjs:246
server.listen(listenTarget.path);
On Windows, codex app-server is spawned through a shell (shell: process.env.SHELL || true,
scripts/lib/app-server.mjs:194), so the chain is
node broker → bash -c "codex app-server" → bash → sh → node codex.js → codex.exe.
Measured time until the endpoint accepts a connection, three consecutive runs on an
otherwise idle machine:
ready within the shipped 2000 ms budget: false
ready within 60000 ms: true (elapsed 3617 ms)
ready within 60000 ms: true (elapsed 2984 ms)
ready within 60000 ms: true (elapsed 3424 ms)
Consequences, all observed:
- The readiness check always times out, so
ensureBrokerSession()returnsnulland
every command silently falls back to a direct app-server. The broker feature never engages. CodexAppServerClient.connect()callsensureBrokerSession(cwd, { env: options.env })
and therefore never supplieskillProcess, so the timeout path does not kill the broker.
It only unlinks the pid file. The broker finishes starting a second later and runs forever.saveBrokerSession()is never reached, so nobroker.jsonis written, and the
SessionEndhook'sbroker/shutdowncannot find the process it should have shut down.
Net effect: one orphaned node app-server-broker.mjs plus its whole codex app-server
tree leaks per companion invocation. After a six-command test sweep we counted seven live
brokers; two more were still holding a worktree from the previous day.
Raising the budget to 15000 ms makes the broker work as designed — broker.json is written
and the same broker is reused by the next invocation — which is how we confirmed defect 2.
2. A live app-server keeps the workspace directory open, so Windows refuses to delete it
The broker is spawned with the workspace as its OS working directory, and passes that same
value to its app-server child:
// scripts/lib/broker-lifecycle.mjs:59-67
const child = spawn(process.execPath, [scriptPath, "serve", "--endpoint", endpoint, "--cwd", cwd, ...], {
cwd, env, detached: true, ...
});
On Windows a process's working directory cannot be deleted, so while that tree is alive the
worktree cannot be removed:
error: failed to delete '.../sandbox-worktrees/wt1': Permission denied
(and git removes the worktree registration regardless, so the directory is left orphaned).
Moving the OS cwd alone is not sufficient. We tested three variants against throwaway
worktrees:
| variant | broker engaged? | git worktree remove |
|---|---|---|
| 1.0.6 as shipped | no (defect 1) | fails — orphaned broker tree holds it |
broker's OS cwd moved, --cwd unchanged |
no | fails — app-server child still on the worktree |
broker and app-server started from the broker's sessionDir |
no | succeeds |
| same, plus a 15000 ms readiness budget so the broker actually engages | yes | fails again |
In the last row nothing on the worktree appears in any command line, yet the directory is
still locked; killing the broker's process tree releases it. So once the broker is genuinely
used, the workspace is held by processes codex itself keeps on the thread's cwd under the
live app-server — not by the broker's own working directory.
What does release it, immediately and cleanly:
shutting down broker pid=... endpoint=pipe:\\.\pipe\cxc-...
shutdown returned after 4 ms
--- removal after shutdown --- → succeeds
What we think the fix looks like
Defect 1 is straightforward: listen before connecting to the app-server (readiness becomes
immediate), or raise the budget and pass killProcess on the timeout path so a broker that
misses it is torn down instead of leaked.
Defect 2 needs the app-server for a workspace to not outlive the work. Either an idle
shutdown once the last client disconnects, or a public opt-out for the broker so each command
uses a direct app-server it closes at the end. Internally disableBroker and a direct path
already exist (scripts/lib/app-server.mjs:338, scripts/lib/codex.mjs:645), but 1.0.6
exposes no setting or flag for it. Setting CODEX_COMPANION_APP_SERVER_ENDPOINT to an
endpoint nobody listens on does reach the direct path via the ENOENT fallback in
withAppServer(), and it does fix the symptom — but it also makes /codex:setup report
loggedIn: false with detail: "connect ENOENT ...", so it is not usable as a workaround.
Changing the OS cwd is not the fix on its own, per the table above.
Reproduction
Windows, any git worktree:
git worktree add ../wt -b topic
node scripts/codex-companion.mjs review --base main --cwd <path-to-wt>
git worktree remove ../wt # Permission denied; registration dropped anyway
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with scripts/lib/broker-lifecycle.mjs, scripts/app-server-broker.mjs, and scripts/lib/app-server.mjs, then run the Windows worktree reproduction command from the issue. Trace broker readiness, timeout cleanup, and app-server lifetime. Done means commands do not leave orphaned processes and the worktree can be removed cleanly after the command exits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100