gpt-5.6-sol: "Script completed" is reported while the nested shell session is still running
What version of Codex CLI is running?
codex-cli 0.145.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Linux 6.12.24-Unraid x86_64 unknown
What terminal emulator and version are you using (if applicable)?
n/a - Codex CLI runs inside a Docker container on Unraid
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1784774745s since unix epoch",
"overallStatus": "warning",
"codexVersion": "0.145.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/home/node/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/home/node/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/home/node/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/home/node/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/home/node/.codex/app-server-daemon/app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "/home/node/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/home/node/.codex",
"config.toml": "/home/node/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/home/node",
"enabled feature flags": "shell_tool, unified_exec, shell_snapshot, code_mode_host, terminal_resize_reflow, sqlite, hooks, enable_request_compression, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, browser_use, browser_use_full_cdp_access, browser_use_external, computer_use, remote_plugin, plugin_sharing, image_generation, resize_all_images, skill_mcp_dependency_install, skill_search, mentions_v2, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, auth_elicitation, personality, fast_mode, tui_app_server, remote_compaction_v2, workspace_dependencies",
"feature flag overrides": "none",
"feature flags enabled": "36",
"log dir": "/home/node/.codex/log",
"mcp servers": "2",
"model": "gpt-5.6-terra",
"model provider": "openai",
"sqlite home": "/home/node/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.39.5",
"details": {
"PATH git #1": "/usr/bin/git",
"PATH git #2": "/bin/git",
"PATH git entries": "2",
"git build options": "git version 2.39.5; cpu: x86_64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh",
"git exec path": "/usr/lib/git-core",
"git version": "git version 2.39.5",
"repo detected": "false",
"selected git": "/usr/bin/git"
},
"remediation": null,
"durationMs": 14
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/usr/local/bin/codex",
"PATH codex #2": "/usr/local/bin/codex",
"PATH codex entries": "2",
"current executable": "/usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex",
"install context": "npm (package /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl, bin /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin, resources /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-resources, path /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path)",
"managed by bun": "false",
"managed by npm": "true",
"managed by pnpm": "false",
"managed package root": "/usr/local/lib/node_modules/@openai/codex",
"npm update target": "/usr/local/lib/node_modules/@openai/codex"
},
"remediation": null,
"durationMs": 74
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "2",
"disabled servers": "0",
"streamable_http servers": "2"
},
"remediation": null,
"durationMs": 3185
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars": "none"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 404)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 658
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "2 IPv4, 2 IPv6, first IPv4",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"models etag present": "true",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 530
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running npm on linux-x86_64",
"details": {
"commit": "unknown",
"current executable": "/usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex",
"install method": "npm (package /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl, bin /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin, resources /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-resources, path /usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path)",
"platform": "linux-x86_64",
"version": "0.145.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "/usr/local/lib/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-path/rg",
"search command readiness": "file exists",
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "Never",
"codex-linux-sandbox helper": "/home/node/.codex/tmp/arg0/codex-arg0NojShT/codex-linux-sandbox",
"execve wrapper helper": "/home/node/.codex/tmp/arg0/codex-arg0NojShT/codex-execve-wrapper",
"filesystem sandbox": "unrestricted",
"network sandbox": "enabled"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/home/node/.codex (dir)",
"active rollout files": "1253 files, 826974886 total bytes, 659995 average bytes",
"archived rollout files": "0 files, 0 total bytes, 0 average bytes",
"goals DB": "/home/node/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "/home/node/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/home/node/.codex/log (dir)",
"memories DB": "/home/node/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"sqlite home": "/home/node/.codex (dir)",
"state DB": "/home/node/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "/home/node/.codex/thread_history_1.sqlite (missing)",
"thread history DB integrity": "skipped (missing)"
},
"remediation": null,
"durationMs": 11907
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "warning",
"summary": "rollout files and state DB thread inventory differ",
"details": {
"default model provider": "openai",
"rollout DB active files": "1253",
"rollout DB active rows": "1231",
"rollout DB archive mismatches": "0",
"rollout DB archived files": "0",
"rollout DB archived rows": "0",
"rollout DB duplicate DB paths": "0",
"rollout DB duplicate rollout thread ids": "0",
"rollout DB malformed file names": "0",
"rollout DB missing active rows": "23",
"rollout DB missing active sample": [
"/home/node/.codex/sessions/2026/07/06/rollout-2026-07-06T09-11-07-019f3832-97a9-7711-a261-b2ca7452ce9f.jsonl",
"/home/node/.codex/sessions/2026/07/06/rollout-2026-07-06T12-09-02-019f38d5-7b28-71c2-8375-d9575962d949.jsonl",
"/home/node/.codex/sessions/2026/07/06/rollout-2026-07-06T13-11-04-019f390e-456e-78d2-bcf1-3a8b90a4cb37.jsonl",
"/home/node/.codex/sessions/2026/07/06/rollout-2026-07-06T14-01-32-019f393c-7b3e-7350-8f94-504093a23807.jsonl",
"/home/node/.codex/sessions/2026/07/06/rollout-2026-07-06T14-07-57-019f3942-5800-7441-b220-9ed6565a1160.jsonl"
],
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=1231",
"rollout DB rows": "1231",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "cli=421, vscode=360, exec=316, subagent:thread_spawn=134",
"rollout DB stale row sample": "/home/node/.codex/sessions/2026/07/16/rollout-2026-07-16T10-55-35-019f6c11-d30b-7253-b714-918f21f81fb3.jsonl",
"rollout DB stale rows": "1"
},
"issues": [
{
"severity": "warning",
"cause": "rollout files are missing from the state DB",
"measured": "23 active, 0 archived",
"expected": "every rollout file has a matching threads row",
"remedy": null,
"fields": []
},
{
"severity": "warning",
"cause": "state DB rows point at missing or unusable rollout files",
"measured": "1 stale rows",
"expected": "every state DB rollout path is a file on disk",
"remedy": null,
"fields": []
}
],
"remediation": null,
"durationMs": 269
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language unavailable",
"details": {
"EDITOR": "not set",
"VISUAL": "not set",
"os": "Debian 12.0.0 [64-bit]",
"os language": "unavailable",
"os type": "Debian",
"os version": "12.0.0"
},
"remediation": null,
"durationMs": 0
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"TERM": "xterm-256color",
"color output": "enabled",
"stderr is terminal": "true",
"stdin is terminal": "true",
"stdout is terminal": "true",
"terminal": "unknown",
"terminal size": "209x51"
},
"remediation": null,
"durationMs": 0
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"terminal title activity": "true",
"terminal title items": "activity, project-name",
"terminal title project source": "cwd",
"terminal title project value": "node",
"terminal title source": "default"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "ok",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.145.0",
"check for update on startup": "true",
"dismissed version": "0.145.0",
"last checked at": "2026-07-23T00:33:57.797494430Z",
"latest version": "0.145.0",
"latest version status": "current version is not older",
"npm update target": "/usr/local/lib/node_modules/@openai/codex",
"update action": "npm install -g @openai/codex",
"version cache": "/home/node/.codex/version.json"
},
"remediation": null,
"durationMs": 400
}
}
}
What issue are you seeing?
On Codex CLI 0.145.0 using gpt-5.6-sol, a single long-running foreground shell command can expose two independently resumable handles:
- an outer JavaScript execution
cell_id, resumed withwait; - an inner shell/PTY
session_id, resumed withwrite_stdin.
The outer JavaScript cell can finish and display Script completed while the nested shell process is still running. The same completed outer result can contain a live SESSION_ID, so the transcript presents completion and non-completion simultaneously.
A representative sequence is:
Script running with cell ID <CELL_ID>
After one or more calls to wait(cell_id=<CELL_ID>):
Script completed
...
SESSION_ID=<SESSION_ID>
At that point, the requested shell command has not necessarily exited. Codex must switch to write_stdin(session_id=<SESSION_ID>, chars="") to retrieve the remaining output and final exit status. When that write_stdin call is itself wrapped in another JavaScript execution, it can produce another outer cell_id.
This causes several problems:
Script completedcan be mistaken for completion of the requested command rather than only completion of the JavaScript wrapper.- One logical foreground command requires switching between two continuation APIs.
- Repeated empty polling consumes additional model/tool turns.
- The final output and exit status can be lost when generated JavaScript forwards only
r.outputand omitsr.session_idorr.exit_code. - Codex can rerun a command after interpreting wrapper completion as process completion.
The command being run is an ordinary synchronous foreground process. It does not background itself or create either continuation identifier.
What steps can reproduce the bug?
Uploaded thread: 019f8ce9-8102-7270-9497-e4d3f5123342
Alternatively:
- Start a new Codex CLI thread using
gpt-5.6-sol.
- Instruct Codex to invoke its code-mode
exectool with exactly this raw
JavaScript, without replacing it with a direct shell invocation:
``javascriptOUTPUT=${r.output ?? ""}
// @exec: {"yield_time_ms": 2000, "max_output_tokens": 2000}
const r = await tools.exec_command({
cmd: "bash -lc 'echo START; sleep 30; echo FINISHED'",
yield_time_ms: 8000,
max_output_tokens: 2000
});
text();SESSION_ID=${r.session_id ?? "null"}
text();EXIT_CODE=${r.exit_code ?? "null"}
text();``
- The timing is deliberate:
``text``
outer exec yield: 2 seconds
inner exec_command yield: 8 seconds
shell command duration: 30 seconds
Therefore the outer JavaScript must yield before the nested shell tool,
and the nested shell tool must yield before the shell process exits.
- Inspect the raw rollout JSONL rather than only the rendered TUI
transcript.
- Observe the outer call first returning:
``text``
Script running with cell ID <CELL_ID>
- Resume it using
wait(cell_id=<CELL_ID>).
- Observe the resolving outer result returning approximately:
``text``
Script completed
OUTPUT=START
SESSION_ID=<SESSION_ID>
EXIT_CODE=null
FINISHED has not been emitted and no exit code exists, so the requested
shell command remains active even though the outer result is labeled
Script completed.
- Continue the returned shell session using:
``javascriptOUTPUT=${r.output ?? ""}
const r = await tools.write_stdin({
session_id: <SESSION_ID>,
chars: "",
yield_time_ms: 30000,
max_output_tokens: 2000
});
text();SESSION_ID=${r.session_id ?? "null"}
text();EXIT_CODE=${r.exit_code ?? "null"}
text();``
- Observe that this later continuation returns
FINISHEDand exit code
0. Confirm from the trace that the original shell command was invoked
only once.
Actual result:
One logical foreground command exposes an outer cell_id resumed throughwait and then an inner session_id resumed through write_stdin. The
outer layer reports Script completed while the inner process is still
running.
Expected result:
The requested command should have one authoritative continuation lifecycle,
or the intermediate status should explicitly distinguishwrapper completed from command still running.
What is the expected behavior?
One requested foreground command should have one authoritative logical lifecycle.
Preferably, Codex should expose one stable continuation identifier from command launch through process exit and internally supervise any nested shell session.
At minimum:
completedshould mean that the requested shell command has terminated, not merely that its JavaScript wrapper returned.- While the shell process remains active, the result should explicitly say that the wrapper completed but the command is still running.
- The response should identify one authoritative next operation instead of requiring the model to infer whether to use
wait(cell_id)orwrite_stdin(session_id). - Incremental output, final output, and the terminal exit code should remain associated with the same logical operation.
- Polling a running shell session should not create another independently exposed continuation lifecycle.
For example, an intermediate state could be represented as:
{
"wrapper_state": "completed",
"process_state": "running",
"session_id": 12345,
"next_operation": "write_stdin"
}
The user-facing status should then say Wrapper completed; command still running, not simply Script completed.
Additional information
I did not explicitly enable code_mode or code_mode_only in my Codex configuration. The behavior occurs with the default tool surface selected for gpt-5.6-sol.
This report is not asserting that Sol's use of code mode is itself a bug. The reported bug is that one requested shell command exposes both the outer JavaScript continuation and the inner process continuation, while the outer wrapper can be described as completed before the requested command has exited.
Related but distinct: #13733 tracks the model-turn and token cost of repeated write_stdin polling. This issue concerns the additional outer cell_id lifecycle, contradictory completion semantics, handle switching, and possible loss of the inner continuation or terminal exit result.
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Related to #33816, but I do not believe this is an exact duplicate.
#33816 reports the model-side consequence: after receiving a live
session_id, GPT-5.6 loses ownership of that process, falsely inferscompletion, and attempts a duplicate
exec_command.This issue reports the underlying execution-interface behavior that can
occur before any duplicate launch: code mode exposes an outer
cell_idand an inner
session_idfor one logical command, and the outer layer canreport
Script completedwhile the inner shell process remains active.The reproduction here requires only one command and remains a defect even
when the model correctly continues the inner session and never reruns the
command. #34866 may explain or contribute to the behavior in #33816, but
the requested runtime fix—one authoritative lifecycle, or explicit
wrapper/process states—is distinct from the model session-ownership
invariant requested there.
I opened #35613 to isolate one independently fixable case within this report: code-mode JavaScript can discard nested
session_idvalues while the unified-exec manager continues to own the live processes.#34866 remains broader and also covers lifecycle contradictions beyond discarded handles. The focused issue restores model-visible handles from existing manager state without redefining wrapper completion or process lifecycle semantics.
The tested implementation retains the originating code-mode
CellIdon manager-owned process entries, queries the existing manager when that exact cell reaches a terminal response, and includes the matching live session IDs in the model-visible status.Technical record:
#35613 seems like a duplicate of #34866.
#34866 already describes the same lifecycle defect: a code-mode wrapper reports
Script completedwhile its nested shell process remains active. It also explicitly identifies the same failure mode isolated in #35613—generated JavaScript can forward onlyr.output, omitr.session_id, and leave the model unable to continue the live process.#35613 provides a narrower reproduction and a concrete implementation strategy—associating manager-owned processes with their originating cell and surfacing their live session IDs. But that is a scoped fix for the defect already reported in #34866, not a separate defect.
The reproduction, tests, and proposed implementation from #35613 are valuable additions here. I agree discussion should be consolidated in #34866 and #35613 closed as a duplicate.
Validation evidence is pinned in two public documents:
The record includes:
76021678checkpoint;eb530466(run 30220464228): formatting, scoped fix, nine focused tests, diff check and clean worktree;TempDirremote exclusion, and 2 compatibility passes;codex-core --libsuite on latest implementation head77e7e3149df366236db2426596c23ebbe1d6bb48(run 30291034837): 2,093 passed, 0 failed, 0 skipped, with artifactcode-mode-full-suite-log;Both Wine attempts completed checkout and Bazel setup but failed during Bazel analysis before any test target was constructed because of the
codex_rust_cratecall-site/signature mismatch tracked in #35683. This preventscodex-command-runnerfrom being declared and stopscore-all-wine-exec-testfrom analyzing. No live-session acceptance assertion or runtime Windows skip executed, so these are reproducible repository build-graph failures rather than implementation-test failures.Windows/Wine boundary: even after that Bazel target becomes runnable, four live-session-handle acceptance cases return early via
skip_if_target_windows!because their nested commands use POSIX shell syntax, and the survivor case returns early viaskip_if_remote!because its hostTempDirpaths are unavailable to remote executors. A successful Wine suite would therefore validate the Bazel/Wine harness and broader shared suite, but would not mean the five live-session-handle acceptance assertions substantively ran against Windows.77e7e314contains the same production implementation as the independently boundedeb530466milestone; its only additional change is test-only Windows-target skip handling for the POSIX acceptance commands.I reproduced the same failure mode on Windows Desktop with a real leaked local process.
Environment
26.820.9563.0gpt-5.6-sol26200, x64functions.execcalling nestedtools.exec_commandObserved incident
A Code Mode cell awaited a nested
tools.exec_command(...). The generated JavaScript then forwarded only:The nested Python command happened to contain an accidental non-terminating loop. After approximately 10.4 seconds, the outer tool result was rendered as:
No
session_idorexit_codewas surfaced to the model, so the turn continued as if the command had completed.The underlying process tree remained alive:
The leaf Python process continuously consumed about 4.2% of total CPU on a 32-logical-processor machine (roughly 1.3 cores) and about 760 MB private memory. It remained active for approximately 2 hours 57 minutes and caused sustained fan noise.
After I identified and terminated only that exact verified process tree, the retained command item finally resolved as failed with a recorded duration of
10,627,370 ms. Total sampled CPU dropped from 19.5% to 12.2%. No matching process remained afterward.Why this evidence is useful
This is not only a confusing status string or a model retry. A normal local foreground command survived for hours because:
exec_commandyielded a live session;output;Script completed; andThe runtime should not emit an unqualified terminal outer status while owned nested work remains live. At minimum, it should preserve and surface the live
session_id/non-terminal state. Preferably, the outer cell should retain ownership and either await the nested session or terminate it on cell/turn abandonment.I have omitted local usernames, project paths, image contents, and raw rollout logs from this public report.
I traced this against current
openai/codexmain (41d3dc56a0, #41239) and found a concrete lifecycle split that matches this report.UnifiedExecProcessManageralready knows whether the nested process is still alive. Afterexec_command, a still-running process returnsProcessStatus::Alivewithprocess_id = Some(...):https://github.com/openai/codex/blob/41d3dc56a0e1de47e30a9585c1b49253c082f8f7/codex-rs/core/src/unified_exec/process_manager.rs#L647-L651
So the manager-side state is internally consistent: the process is not done. The contradiction appears because wrapper/code-mode completion is a second lifecycle view that is not required to agree with that
Alivestate. In other words,Script completedcan be emitted while unified-exec still owns a continuation handle.I built an observation-only reproduction around that exact boundary (it does not patch Codex behavior):
It reports the disagreement as
wrapper_complete_process_running.A narrow invariant that seems sufficient for this failure mode is: if unified-exec reports
ProcessStatus::Alive/ a liveprocess_id, wrapper/code-mode must not represent the logical command as completed, and the continuation handle must remain model-visible.Full RCA / reproducible research artifact: https://github.com/byte271/codex/blob/main/codex-agent-kernel/docs/UPSTREAM.md
This is issue evidence only, not an external PR proposal.