[codex plugin 1.0.2] Broker IPC hangs on Windows (~20% failure rate) — skill dispatch never completes

Resolved 💬 3 comments Opened Apr 15, 2026 by kevin570kk Closed Apr 19, 2026

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Claude Code: 2.1.109
  • Shell: Git Bash
  • Node: v24.14.0
  • codex plugin: openai-codex/codex v1.0.2 (installed 2026-04-04)
  • codex CLI: 0.118.0 (npm global)

Summary

Invoking /codex:rescue, /codex:review, /codex:adversarial-review through the skill dispatch hangs indefinitely about 20% of the time. The turn never completes — no output, no error, no timeout. Direct invocation of codex exec from Git Bash always works.

Observed Behavior

  • Skill dispatch: ~80% succeed normally, ~20% hang for >20 minutes with no output
  • No stderr, no recovery — user must abandon the turn
  • Happens across all three commands (rescue, review, adversarial-review)
  • Over one day of normal use: 80+ invocations, 18+ hangs

Forensic Evidence

After a hang, the following zombie state is left behind:

  1. Stale broker directory /tmp/cxc-<id>/ persists with:
  • broker.pid containing a PID that is no longer alive
  • broker.log containing only deprecation warnings, no IPC activity
  • No *.sock file (socket never bound, or cleaned on crash)
  1. Empty state directory /tmp/codex-companion/<workspace-hash>/ exists but jobs/ was never created — indicating writeJobFile() was never reached.
  2. PID in broker.pid is a genuine zombiekill -0 $PID returns failure, but directory is not cleaned.

Root Cause Analysis

Tracing through the plugin source (paths under ~/.claude/plugins/cache/openai-codex/codex/1.0.2/):

User /codex:rescue
  → skill wrapper (rescue.md)
    → node codex-companion.mjs task
      → withAppServer(cwd, fn)                    [codex.mjs:607]
        → CodexAppServerClient.connect(cwd)        [codex.mjs:610]
          → ensureBrokerSession(cwd)               [broker-lifecycle.mjs:113]
            → spawnBrokerProcess()                 [broker-lifecycle.mjs:59-70]
            → waitForBrokerEndpoint(2000ms)        [broker-lifecycle.mjs:149]
              ⨯ HANGS HERE on Windows

Hypotheses (Windows-specific)

  1. Unix socket path emulation: brokerEndpoint at /tmp/cxc-*/*.sock is created under Windows' emulated /tmp. Under Node.js on Windows, unix socket connection is unreliable — especially when the broker child process is spawned with detached: true, stdio: ["ignore", logFd, logFd] (broker-lifecycle.mjs:59-70). File descriptor writes are not atomic on Windows.
  2. Line buffering + CRLF: handleLine() in app-server.mjs:117-128 splits JSON-RPC messages on \n. If the broker emits \r\n on Windows, upstream parsers may drop notifications — including the completion signal.
  3. Race condition in scheduleInferredCompletion() (codex.mjs:367-388): uses a 250ms setTimeout to detect "inferred completion" against state.pendingCollaborations and state.activeSubagentTurns. A missed notification + stale set membership → turn never completes → captureTurn() (codex.mjs:553-605) never resolves.
  4. Broker race on cold start: first connection after a new broker spawn is where the 20% failure clusters. Warm broker (previous task succeeded) tends to stay stable.

Minimal Repro Attempt

No reliable repro — it's intermittent. Rough steps:

  1. Leave codex plugin idle for 30+ minutes (broker dies)
  2. Invoke /codex:rescue "any prompt"
  3. ~1 in 5 invocations hangs indefinitely; rest complete normally

Workaround

Bypass the skill entirely and call codex exec directly from bash. Example wrapper:

\\\`bash

direct codex exec + zombie cleanup

shopt -s nullglob
for dir in /tmp/cxc-*; do
PID=\$(cat "\$dir/broker.pid" 2>/dev/null)
if [[ -n "\$PID" ]] && kill -0 "\$PID" 2>/dev/null; then continue; fi
AGE_MIN=\$(( ( \$(date +%s) - \$(stat -c %Y "\$dir") ) / 60 ))
[[ \$AGE_MIN -ge 30 ]] && rm -rf "\$dir"
done
codex exec "\$PROMPT"
\\\`

This is stable across 100+ invocations, confirming the issue is in the broker/IPC layer, not codex CLI itself.

Requested Fix Direction

  1. Treat waitForBrokerEndpoint() timeout as fatal — surface an error to Claude Code instead of hanging Promise forever
  2. Add CRLF normalization in handleLine() before JSON-RPC parse
  3. On Windows, fall back from unix socket to named pipe (\.\pipe\codex-*) which is native Windows IPC and better supported by Node.js
  4. Add pre-flight cleanup of stale /tmp/cxc-* directories in ensureBrokerSession()

Impact

For users running Claude Code on Windows, this silently burns 20% of codex-related interactions. Hard to diagnose without plugin internals, and no user-visible error means users often blame their own prompts.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗