codex exec --json resume can hang indefinitely on macOS after MCP helpers start

Open 💬 2 comments Opened Mar 12, 2026 by wangqianqianjun

What version of Codex CLI is running?

codex-cli 0.114.0

What subscription do you have?

ChatGPT login via the local CLI. I did not verify the exact plan before filing this.

Which model were you using?

gpt-5.4

What platform is your computer?

Darwin 24.6.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

This repro comes from a non-interactive integration that spawns codex exec --json ... resume ... as a child process on macOS, not from an interactive terminal session.

What issue are you seeing?

I am seeing a silent hang on codex exec --json resume <session> <follow-up prompt> on macOS.

What makes this one tricky is that the process does not fail fast and it does not look like a normal "tool call hung" case. The resumed codex process starts, launches MCP helper processes, and then just sits there for hours without emitting a new turn start, final answer, or completion event.

In my case:

  • the resumed process stays alive until manually killed
  • helper processes for chrome-devtools-mcp, playwright-mcp, and mcp-server-fetch are also still alive
  • the rollout file never gets a new event for the resumed turn; the last persisted events are still from the previous completed turn
  • sampling the stuck main process shows it mostly sleeping in __psynch_cvwait, kevent, and read, which looks more like an internal wait on IO/transport than active model work

This feels related to MCP startup / tool inventory, not to a specific tool call after the turn has already started.

It also seems close to #14115 and #6664, but not identical. In this case the hang appears earlier: during resumed-turn startup, before I see a new TurnStarted-style event or any MCP tools/call.

What steps can reproduce the bug?

I have not reduced this to the smallest possible repro yet, but this is the pattern that reproduced reliably for me:

  1. Use Codex CLI on macOS arm64 with several MCP servers configured. In my case those included:
  • chrome-devtools
  • chrome_devtools
  • fetch
  • playwright
  • stitch
  1. Start a normal session and let it complete at least one turn so the session has resumable history.
  2. Run a follow-up through resume, for example:
codex exec --json \
  --dangerously-bypass-approvals-and-sandbox \
  --skip-git-repo-check \
  --cd /redacted/worktree \
  resume <session-id> 'follow-up prompt'
  1. Observe that:
  • the main codex process starts normally
  • MCP helper processes start
  • no new JSON events arrive for the resumed turn
  • no final answer or completion event is produced
  • the process remains stuck indefinitely

What is the expected behavior?

One of these should happen:

  • the resumed turn should start normally and emit its usual events, then finish
  • or Codex should surface a real startup error
  • or Codex should time out and fail explicitly instead of waiting forever

In other words, resume should not be able to wedge the whole process in a no-output state just because some internal startup path never resolves.

Additional information

I dug through the current source before filing this, and the most suspicious path I found is:

  • built_tools(...) calls mcp_connection_manager.list_all_tools()
  • startup prewarm and normal turn setup both use built_tools(...)
  • list_all_tools() awaits each MCP client
  • AsyncManagedClient::listed_tools() can await the client startup future directly when there is no startup snapshot available
  • there is already a unit test named list_all_tools_blocks_while_client_is_pending_without_startup_snapshot

That makes me suspect a resumed turn can stall indefinitely if one MCP client startup future never resolves, even though the helper process itself has already been spawned.

The other clue is that exec waits for events with no obvious overall timeout, so if startup gets wedged before the resumed turn really begins, the CLI looks completely silent from the outside.

I intentionally redacted private repo paths, prompt text, session ids, and any local secrets from this report. If useful, I can follow up with sanitized logs or help test a patch with extra instrumentation around MCP initialize / tools listing during resume.

View original on GitHub ↗

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