openai / openai/codex-plugin-cc
Sequential task calls fail ~50% with "codex app-server connection closed" — broker reused after single-turn exit, shouldRetryDirect misses clean close
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Sequential codex-companion task calls fail ~50% of the time with
codex app-server connection closed, in a near-perfect pass / fail / pass / fail
alternation. This is single-session, non-concurrent — one call at a time — and is
not project-specific (reproduces from any cwd, including $HOME).
Auth//codex:setup is healthy, and a directly-spawned codex app-server
(initialize + thread/start) is 100% reliable, so the app-server itself is fine — the
failure is in broker reuse + the direct-retry fallback.
Env: plugin openai/codex-plugin-cc v1.0.5 (latest) · codex-cli 0.142.1 ·
Node v22.22.3 · macOS arm64.
Steps to reproduce
cd ~ # any directory; no project .codex needed
for i in $(seq 1 6); do
node <plugin>/1.0.5/scripts/codex-companion.mjs task --fresh --effort low "Reply: $i"
done
# => ~3/6 fail with "codex app-server connection closed", alternating ok/FAIL/ok/FAIL/...
Forcing a fresh broker each run (delete state/<slug>-<hash>/broker.json before each
call) → 6/6 pass. That isolates the cause to broker reuse.
Root cause
task connects through the broker: CodexAppServerClient.connect() →
ensureBrokerSession(). The broker process exits after serving a single turn, but
its broker.json + unix socket linger briefly:
- Run N — no live broker → spawn fresh broker → turn succeeds. Broker lingers.
- Run N+1 —
isBrokerEndpointReady()still connects/initializes against that broker,
so it is reused → the broker drops the second turn →
AppServerClientBase.handleExit(null)(scripts/lib/app-server.mjs:172) →
Error("codex app-server connection closed.")with norpcCodeand nocode.
This failure tears down the broker. - Run N+2 — broker gone → fresh spawn → succeeds. → the alternation.
The existing direct-mode fallback in withAppServer()
(scripts/lib/codex.mjs:620-633) does not catch this. shouldRetryDirect only
triggers on BROKER_BUSY_RPC_CODE, ENOENT, or ECONNREFUSED — never on a clean
"connection closed" mid-turn — so the error is thrown to the caller instead of retrying
direct:
const shouldRetryDirect =
(client?.transport === "broker" && error?.rpcCode === BROKER_BUSY_RPC_CODE) ||
(brokerRequested && (error?.code === "ENOENT" || error?.code === "ECONNREFUSED"));
Suggested fix (verified locally)
Extend shouldRetryDirect to also retry direct when a broker connection drops:
const brokerConnectionDropped =
client?.transport === "broker" &&
(error?.code === "ECONNRESET" ||
error?.code === "EPIPE" ||
/connection closed|exited unexpectedly/i.test(error?.message ?? ""));
const shouldRetryDirect =
(client?.transport === "broker" && error?.rpcCode === BROKER_BUSY_RPC_CODE) ||
brokerConnectionDropped ||
(brokerRequested && (error?.code === "ENOENT" || error?.code === "ECONNREFUSED"));
After this patch, with no broker clearing: 6/6 and 5/5 consecutive runs pass across
two different cwds. This just restores the fallback's own intent. A more thorough fix
could additionally avoid reusing a broker that is shutting down (e.g. liveness/age check
in isBrokerEndpointReady/ensureBrokerSession), but the broker is alive enough to
accept+initialize at reuse time, so the direct-retry is what actually unblocks it.
Related but distinct
- #286 (Race 3) touches
ensureBrokerSession/broker.json, but is about concurrent
same-cwd invocations. This report is purely sequential — no race. - #108 / #380 are about broker cleanup/orphans on session exit; this is about reuse
dropping the next turn during normal use. - #202 is a zombie job record (
jobs.json, "Task is still running"), a different
failure path and error message.
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 in scripts/lib/codex.mjs around lines 620-633, then inspect scripts/lib/app-server.mjs around line 172 to understand how a broker connection close is surfaced. Run the sequential reproduction from any cwd and compare it with runs that clear broker.json. Done means consecutive task calls no longer fail when a reused broker closes, with the direct fallback handling that failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100