Repository navigation
[URGENT] Stable IDE extension ships a prerelease CLI with under-development "Code mode" silently active, breaking any long-running MCP calls and burning tokens while waiting #29122
Description
What version of the IDE extension are you using?
26.616.31447 (bundled codex-cli 0.142.0-alpha.1)
What subscription do you have?
Plus
Which IDE are you using?
VS Code
What platform is your computer?
Microsoft Windows NT 10.0.26100.0 x64
What issue are you seeing?
The just-released, non-prerelease VS Code extension 26.616.31447 bundles codex-cli 0.142.0-alpha.1 and routes MCP tools through code-mode exec cells even though I never enabled or opted into code mode. This silently changes the execution contract in four serious ways:
- an under-development mode is activated without consent
- the model now controls whether a call remains alive
- merely waiting for long-running mcp call return consumes tokens and context
- the only available mitigation depends on an undocumented Codex-generated namespace that MCP servers cannot control.
In detail:
1. Under-development code mode is activated without opt-in
The published Codex sample configuration describes code mode as "under development and off by default." At the time of the failure, my config did not contain [features.code_mode], the affected binary reported code_mode ... false in codex features list, and the installed IDE extension was a regular release rather than a prerelease. Nevertheless, the affected IDE thread exposed MCP tools only through code-mode exec.
The extension itself is not marked as a preview or prerelease, but it bundles a CLI whose codex-package.json version is 0.142.0-alpha.1; OpenAI's corresponding rust-v0.142.0-alpha.1 release is explicitly classified as a prerelease. The IDE does not offer a separate bundled-CLI channel selection, so this prerelease executable arrived through the regular extension without any user opt-in.
Codex PR #25031 indicates that model-catalog tool_mode metadata can select code_mode or code_mode_only and override local feature flags. If that mechanism caused this behavior, an under-development execution mode with materially different tool-lifetime and token semantics was activated remotely without user consent, a visible migration warning, or an obvious local control that takes precedence.
2. MCP call lifetime is now controlled by the model for all MCPs by default
Previously, a direct MCP call remained blocked in the runtime until the tool completed, the configured tool_timeout_sec expired, the transport failed, or the user cancelled it. The model had no decision point while the call was pending.
In code mode, exec yields while the nested MCP call is still healthy. After every yield, the model must choose whether to issue another wait or terminate: true. The MCP server cannot set yield_time_ms, cannot prevent termination, and cannot tell the code-mode runtime that a multi-hour duration is expected. The configured MCP timeout remains only an upper bound; it no longer gives the call an uninterrupted runtime wait up to that deadline. Correct call lifetime now depends on the model repeatedly making the right scheduling decision without enough information to do so reliably.
To be precise, tool_timeout_sec itself is still user-configured and host-enforced as a maximum; it is not selected by the model. What the model now controls is each code-mode yield_time_ms and the decision to call terminate: true. There is no MCP server option that binds those decisions to tool_timeout_sec or prevents an earlier model-authored termination.
3. Waiting is now a token-consuming model loop
The initial exec yield defaults to 10 seconds unless the model writes a different yield_time_ms pragma. Every subsequent wait interval is also selected by the model and independently defaults to 10 seconds. Each yield produces a model-visible tool output such as Script running with cell ID ..., triggers another model inference, and appends both the wait call and its output to the conversation history.
A 10-hour MCP call using the 10-second defaults can therefore create roughly 3,600 additional model turns and 3,600 persistent call/output pairs. The old direct-MCP path used no model turns or intermediate transcript tokens while the call was pending. The new path can consume token/rate limits, grow the context until compaction or exhaustion, and expose the healthy call to thousands of unnecessary model-authored termination decisions.
This looks designed for interactive orchestration, but long-running blocking tools were not handled safely.
4. The mitigation depends on an unstable, under-documented, internal mcp___ prefix / namespace.
The only deterministic mitigation currently available is features.code_mode.direct_only_tool_namespaces. It requires an exact model-visible namespace such as mcp__ask_human, but that is not a name published by the MCP server. Codex derives it from the client-side [mcp_servers.<name>] key, replaces unsupported characters, adds an internally generated mcp__ prefix, and then performs an exact string match. The MCP server cannot discover the resulting name, request direct execution, or declare that its calls are intentionally long-running.
This derivation is not defined in the MCP documentation as a stable contract. The official sample contains an example value but does not explain how users or server authors should determine it. The current Codex source calls mcp__ a historical prefix, and the affected CLI exposes a separate under-development non_prefixed_mcp_tool_names feature. A naming change could therefore silently stop the mitigation from matching.
Requiring every user to reverse-engineer and maintain a client-generated namespace is not a safe design for preserving tool-call lifetime. This must either remain runtime-controlled by the configured timeout and explicit user cancellation, or Codex needs a documented, discoverable, stable configuration contract and an MCP-level mechanism through which a server can declare the required execution behavior.
Concrete failure reproducer
The concrete reproducer was ask-human - a human-in-the-loop MCP that is intentionally designed to keep a tool call pending as many times as the user needs before providing the answer. It makes all four problems easy to observe, though none is specific to that tool: the same code-mode control path can affect any long-running MCP operation, such as a deployment, data import, remote job, or approval workflow.
In the example given, the MCP tool and Codex MCP client were both configured with explicit 86400-second timeouts. The user did not answer or cancel the prompt, and the MCP tool did not return a timeout or error.
After an intermediate 240-second wait yielded Script running with cell ID 6, the model issued a second wait call with terminate: true. This cancelled the still-valid MCP call and the agent then repeated the question as ordinary chat text.
The VS Code UI displayed Tool returned no content for the nested MCP call even though the rollout recorded that the exec cell was still running.
Sanitized rollout timeline (UTC):
14:35:45 exec starts tools.mcp__ask_human__ask_human(...)
14:35:55 exec yields: Script running with cell ID 6
14:35:58 wait({ cell_id: "6", yield_time_ms: 240000 })
14:39:58 wait yields: Script running with cell ID 6
14:40:07 wait({ cell_id: "6", terminate: true, ... })
14:40:07 Script terminated
14:40:19 agent ends turn and repeats the unanswered question
There is no mcp_tool_call_end event for the failed call. The explicit terminate: true call is model-authored in the rollout; it was not caused by a user action or MCP timeout.
What steps can reproduce the bug?
-
Configure a stdio MCP server with any tool that legitimately runs longer than 4 minutes. A human-approval tool like this one is a convenient reproducer, but the operation can also be a long-running job with deterministic completion.
-
Set
tool_timeout_secto a long value, for example:[mcp_servers.example] command = "..." tool_timeout_sec = 86400
-
Start a new Codex IDE thread using extension
26.616.31447, bundled CLI0.142.0-alpha.1, and GPT-5.5 with xhigh reasoning. -
Prompt Codex in a way so that it calls the long-running MCP tool. In this configuration Codex invokes it through code mode.
-
Do not complete or cancel the MCP operation for at least 4 minutes.
Anyway, this appears to be model-dependent rather than a deterministic runtime timeout. In a second 0.142.0-alpha.1 session, answering within 58 seconds succeeded after two model-authored 30-second wait calls. That successful call still required extra model turns solely to keep waiting. The core regression is that every code-mode yield both consumes another inference and creates another decision point at which the model can terminate a healthy nested tool.
What is the expected behavior?
An under-development execution mode must not be silently enabled in a regular IDE release when the user has not opted in and the local feature state reports it as disabled. If remote model metadata can select code mode, that behavior and its changed tool-execution semantics need to be documented, visible, and subject to a reliable user-controlled override.
A safety-critical override must not depend on reverse-engineering an undocumented generated namespace. Codex should either preserve direct runtime-managed behavior automatically for pending MCP calls, provide a stable and discoverable client configuration keyed by the configured MCP server name, or support server-declared execution requirements through the protocol.
Codex must not terminate a nested MCP call solely because an exec/wait interval yielded while the call is still running. It should preserve the call until one of these occurs:
- the nested tool completes;
- the configured MCP timeout actually expires;
- the user explicitly cancels; or
- the transport reports a real failure.
Long-running nested tool calls should ideally remain runtime-managed rather than requiring repeated model decisions. At minimum, a running-cell yield should be unambiguously represented to both the model and UI as an intermediate state, not Tool returned no content.
No model inference should be required merely because a tool is still running, and runtime status updates should not accumulate as model-visible conversation history. The configured MCP timeout should remain the host-side deadline unless the user cancels or a real failure occurs.
Additional information
- The official sample config calls code mode "under development and off by default," but the affected thread used it while the local feature state reported it disabled. PR [codex] Add model tool mode selector #25031 documents model-catalog tool-mode selection overriding feature flags, which appears to explain the otherwise unexpected activation.
- With extension
26.609.30741/codex-cli 0.140.0-alpha.2, Ask Human was invoked as a direct MCP call. Recorded calls remained pending and then completed successfully after the user responded 65, 88, and 171 minutes later, without an intervening model decision about whether to terminate them. - Extension
26.616.30709also bundlescodex-cli 0.142.0-alpha.1, but I did not reproduce the failure on that exact extension build. The affected run was on26.616.31447. - Neither the MCP server nor
tool_timeout_seccontrols code-modeyield_time_ms. The model authors the optional initialexecpragma and every subsequentwaitcall; omitted values independently default to 10 seconds. - The affected
0.142.0-alpha.1binary contains the under-developmentfeatures.code_mode.direct_only_tool_namespacesoption, which can keep a configured MCP namespace out ofexec. This is a useful per-client mitigation for known special cases, but the MCP server cannot request it, users must identify and configure each namespace manually, and it does not fix the generic long-running MCP execution regression. - After adding
direct_only_tool_namespaces = ["mcp__ask_human"]and restarting the IDE session, the same tool was invoked directly, remained pending for 298.6 seconds, and completed successfully without a code-mode yield,waitcall, or intervening model decision. This confirms that the mitigation works in the affected build while also demonstrating that correct lifetime now depends on per-client knowledge of the generated namespace. - MCP progress notifications are not a mitigation: a server can report that work is still pending when the client supplies a progress token, but those notifications do not control code-mode yields, prevent
terminate: true, or bind the cell lifetime totool_timeout_sec. - The MCP server's own 86400-second deadline was shown to the user and had not expired.
- A local workaround is to clarify agent instructions that
Script running with cell ID ...is not a tool timeout and must not triggerterminate: true. - I also rolled back and pinned extension version
26.609.30741.
A full session ID and raw rollout can be supplied privately
Activity
Necmttn commentedon Jun 21, 2026
This crosses two contracts: extension release channel and tool execution mode. Codex could record a per-turn execution contract: extension version, bundled CLI version, CLI prerelease flag, code_mode configured/effective, MCP transport path, and wait token/cost attribution. If the stable extension activates prerelease code-mode routing, the UI should surface that before a tool call starts, especially for long-running MCP calls.
Generated with ax.
rusproject commentedon Jun 22, 2026
Any update on this?
cxd40 commentedon Jul 14, 2026
I can reproduce a closely related failure in the current Windows Codex Desktop app, and it is not limited to the VS Code extension or a long-running MCP server.
Environment
26.707.9564.00.144.210.0.26200, x64gpt-5.6-sol, xhigh reasoning for the affected turncode_mode=false,code_mode_host=truecode_modeopt-in inconfig.toml-c features.code_mode_host=trueFailure variant
This involved an ordinary PowerShell verification command invoked through a code-mode
execcell. The command ran eight bounded HTTP checks and legitimately took longer than the initial 10-second yield.Sanitized timeline (local time, UTC+8):
The turn remained
inProgressfor roughly 34 minutes. The command itself was healthy and completed successfully. The local app-server and MCP connection-manager continued producing heartbeat/trace activity during the gap, and there were noWARNorERRORrecords for the affected thread. Network connectivity was also confirmed after recovery.The key difference from the original report is that the model did not issue
terminate: true; it simply failed to issue the requiredwaitafter receivingScript running with cell ID 61. A user follow-up caused the same turn to resume immediately.Why this appears to be the same execution-contract problem
A yielded cell requires another model inference merely to keep a healthy command alive and collect its result. If that inference stalls or omits
wait, Desktop leaves the turn visually active indefinitely even though the underlying command has completed. The UI exposes no bounded timeout, automatic result reconciliation, or recovery action.This suggests the runtime should own pending-cell continuation and result collection. At minimum:
inProgress.Local mitigations applied
Script running with cell ID ...must always be followed by explicitwaitcalls.queuetosteer, so a status/continue message can reach an active stuck run without requiring a manual pause.These reduce the impact but do not fix the runtime-level failure. I can provide a redacted transcript/log excerpt privately if useful.
shirafukayayoi commentedon Jul 23, 2026
Adding a current Codex Desktop / Windows data point where the code-mode lifetime problem compounds with
multi_agent_v1and creates duplicate subagents plus a high-token polling loop.Environment
This installation uses a custom local Responses base URL. Provider requests completed successfully with unique request/tool-call IDs; the failing
exec,wait, andmulti_agent_v1sequence occurred in the local Codex Desktop tool host.Observed sequence
The parent spawned three bounded PDF-inspection subagents.
The user asked to replace those workers with a vision-capable model.
close_agentsuccessfully shut down the original three workers.The first replacement spawn failed schema validation, then a corrected three-agent spawn batch was run inside code-mode
exec.The outer
execyielded after about 11 seconds:Instead of preserving and waiting on cell 19, later model turns started additional spawn attempts for the same three page ranges. This created another worker set and eventually produced:
Once workers existed, the parent repeatedly wrapped
multi_agent_v1__wait_agentin newexeccalls. Each outer call yielded after about 11 seconds and allocated another cell:The parent emitted essentially the same progress message about switching models and waiting for the same page ranges about 30 times in roughly eight minutes.
At least one subagent later delivered a valid completion notification. The parent nevertheless attempted to wait on an earlier completed cell and received:
By that point the parent session had consumed approximately 3.5 million input tokens, primarily cached context, without reaching the integration/write step.
The repeated waits were not repeated user requests. The only user follow-up was the one model-switch request.
Why this appears related
This is the same architectural failure described here: a healthy long-running nested operation is converted into a model-visible
execcell, every yield forces another inference, and correctness depends on the model preserving exactly one cell/operation identity across turns. Withmulti_agent_v1, a wrong decision is more damaging than terminating one MCP call: it can duplicate spawn batches, fill the agent pool, create many simultaneous wait cells, and repeatedly burn the full parent context.It also overlaps with:
waitretries after a cell is no longer available)waitrouting loops with stalled subagents)This variant differs because the parent often starts a fresh nested
wait_agentoperation and therefore receives a fresh exec cell ID each time, rather than only retrying one stale/noop cell.Expected behavior / possible fix shape
multi_agent_v1lifecycle tools, especiallyspawn_agent,wait_agent, andclose_agent, should not require model-driven code-mode cell keepalive loops.I can provide a more heavily redacted rollout excerpt if maintainers need the exact event ordering.