/fork leaves parent thread with active writer, preventing resume in another terminal
Open 💬 6 comments Opened Aug 12, 2026 by pkhafizov
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
What version of Codex CLI is running?
codex-cli 0.147.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Linux 5.15.167.4-microsoft-standard-WSL2 x86_64 x86_64
What terminal emulator and version are you using (if applicable)?
WSL
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1786518424s since unix epoch",
"overallStatus": "warning",
"codexVersion": "0.147.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/home/[REDACTED_USER]/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/home/[REDACTED_USER]/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/home/[REDACTED_USER]/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/home/[REDACTED_USER]/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/home/[REDACTED_USER]/.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 env vars present": "OPENAI_API_KEY",
"auth file": "/home/[REDACTED_USER]/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "[REDACTED: identity metadata]",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/home/[REDACTED_USER]/.codex",
"config.toml": "/home/[REDACTED_USER]/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/home/[REDACTED_USER]/code/ai-content-pipeline",
"enabled feature flags": "shell_tool, view_image, unified_exec, shell_snapshot, code_mode_host, terminal_resize_reflow, sqlite, memories, hooks, enable_request_compression, network_proxy, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, in_app_updates, browser_use, browser_use_full_cdp_access, browser_use_external, computer_use, remote_plugin, plugin_sharing, image_generation, resize_all_images, item_ids, 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": "memories=true, network_proxy=true",
"feature flags enabled": "41",
"log dir": "/home/[REDACTED_USER]/.codex/log",
"mcp servers": "[REDACTED: MCP server names]",
"model": "gpt-5.6-sol",
"model provider": "openai",
"sqlite home": "/home/[REDACTED_USER]/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.43.0",
"details": {
".git entry": "[REDACTED: repository metadata]",
"PATH git #1": "/usr/bin/git",
"PATH git #2": "/bin/git",
"PATH git entries": "2",
"git branch": "[REDACTED: branch name]",
"git build options": "git version 2.43.0; 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.43.0",
"repo detected": "true",
"repo root": "[REDACTED: repository path]",
"selected git": "/usr/bin/git"
},
"remediation": null,
"durationMs": 79
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #2": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #3": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #4": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #5": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #6": "/home/[REDACTED_USER]/.local/bin/codex",
"PATH codex #7": "/mnt/c/nvm4w/nodejs/codex",
"PATH codex #8": "/mnt/c/nvm4w/nodejs/codex",
"PATH codex entries": "8",
"current executable": "/home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/bin/codex",
"install context": "standalone (unix, package /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl, bin /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/bin, resources /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/codex-resources, path /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/codex-path)",
"managed by bun": "false",
"managed by npm": "false",
"managed by pnpm": "false",
"managed package root": "not set"
},
"remediation": null,
"durationMs": 23
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "[REDACTED: MCP server names]",
"disabled servers": "[REDACTED: MCP server names]",
"stdio servers": "[REDACTED: MCP server names]",
"streamable_http servers": "[REDACTED: MCP server names]"
},
"remediation": null,
"durationMs": 3481
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars": "[REDACTED: proxy configuration]"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"openai API base URL": "https://api.openai.com/v1 reachable (HTTP 404)",
"openai API route probe": "https://api.openai.com/v1/<redacted> route exists (HTTP 401)",
"reachability mode": "API key auth"
},
"remediation": null,
"durationMs": 682
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "[REDACTED: DNS result]",
"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": "[REDACTED: proxy configuration]",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 1620
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on linux-x86_64",
"details": {
"commit": "unknown",
"current executable": "/home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/bin/codex",
"install method": "standalone (unix, package /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl, bin /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/bin, resources /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/codex-resources, path /home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-x86_64-unknown-linux-musl/codex-path)",
"platform": "linux-x86_64",
"version": "0.147.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "/home/[REDACTED_USER]/.codex/packages/standalone/releases/0.147.0-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": "OnRequest",
"codex-linux-sandbox helper": "/home/[REDACTED_USER]/.codex/tmp/[REDACTED]",
"execve wrapper helper": "/home/[REDACTED_USER]/.codex/tmp/[REDACTED]",
"filesystem sandbox": "restricted",
"network sandbox": "restricted"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/home/[REDACTED_USER]/.codex (dir)",
"active rollout files": "1404 files, 1578995851 total bytes, 1124640 average bytes",
"archived rollout files": "0 files, 0 total bytes, 0 average bytes",
"goals DB": "/home/[REDACTED_USER]/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "/home/[REDACTED_USER]/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/home/[REDACTED_USER]/.codex/log (dir)",
"memories DB": "/home/[REDACTED_USER]/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"queue DB": "/home/[REDACTED_USER]/.codex/queue_1.sqlite (file)",
"queue DB integrity": "ok",
"sqlite home": "/home/[REDACTED_USER]/.codex (dir)",
"standalone release cache": "3 entries in /home/[REDACTED_USER]/.codex/packages/standalone/releases",
"state DB": "/home/[REDACTED_USER]/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "/home/[REDACTED_USER]/.codex/thread_history_1.sqlite (file)",
"thread history DB integrity": "ok"
},
"remediation": null,
"durationMs": 4889
},
"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": "1400",
"rollout DB active rows": "1395",
"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": "5",
"rollout DB missing active sample": [
"[REDACTED: rollout path and identifier]"
],
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=1395",
"rollout DB rows": "1395",
"rollout DB scan cap reached": "false",
"rollout DB scan error sample": [
"[REDACTED: rollout path and identifier]"
],
"rollout DB scan errors": "4",
"rollout DB sources": "cli=448, subagent:thread_spawn=407, vscode=340, subagent:review=188, exec=12",
"rollout DB stale rows": "0"
},
"issues": [
{
"severity": "warning",
"cause": "rollout files are missing from the state DB",
"measured": "5 active, 0 archived",
"expected": "every rollout file has a matching threads row",
"remedy": null,
"fields": []
},
{
"severity": "warning",
"cause": "rollout scan was incomplete or found bad files",
"measured": "4 scan errors, 0 malformed names, scan cap reached: false",
"expected": "rollout directories are fully scannable",
"remedy": "Check file permissions and unexpected files under CODEX_HOME sessions.",
"fields": []
}
],
"remediation": null,
"durationMs": 988
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language C",
"details": {
"EDITOR": "not set",
"LANG": "C.UTF-8",
"VISUAL": "not set",
"os": "Ubuntu 24.4.0 (noble) [64-bit]",
"os language": "C",
"os type": "Ubuntu",
"os version": "24.4.0"
},
"remediation": null,
"durationMs": 4
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"DISPLAY": "[REDACTED: local display endpoint]",
"TERM_PROGRAM": "vscode",
"VSCODE_IPC_HOOK_CLI": "[REDACTED: local IPC path]",
"WAYLAND_DISPLAY": "[REDACTED: local display endpoint]",
"WSL_DISTRO_NAME": "present",
"WSL_INTEROP": "[REDACTED: local IPC path]",
"color output": "enabled",
"effective locale": "C.UTF-8",
"stderr is terminal": "true",
"stdin is terminal": "true",
"stdout is terminal": "true",
"terminal": "VS Code",
"terminal size": "306x37",
"terminal version": "1.132.1"
},
"remediation": null,
"durationMs": 0
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"redacted": "[REDACTED: terminal project metadata]"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "ok",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.147.0",
"check for update on startup": "true",
"last checked at": "2026-08-12T06:19:22.738664369Z",
"latest version": "0.147.0",
"latest version status": "current version is not older",
"update action": "standalone installer",
"version cache": "/home/[REDACTED_USER]/.codex/version.json"
},
"remediation": null,
"durationMs": 783
}
}
}
What issue are you seeing?
/fork leaves the parent session with an active writer, so the parent session cannot be resumed in another terminal.
I am using Codex CLI 0.147.0.
Steps to reproduce:
- Start working in a Codex CLI session.
- Run:
/fork
- Codex switches to a new forked session.
- Run:
/status
The result confirms that the current TUI is attached to the new forked session:
Session: 019ff4b6-6708-7830-8f43-11ce2495c89f
Forked from: 019ff4a0-1d4d-79a3-a456-ab7187a79d02
After /fork, Codex also prints:
To continue this session, run codex resume 019ff4a0-1d4d-79a3-a456-ab7187a79d02
- In another terminal, run exactly that command:
codex resume 019ff4a0-1d4d-79a3-a456-ab7187a79d02
Instead of resuming the parent session, Codex fails with:
Error: Failed to resume session from /home/pavel/.codex/sessions/2026/08/12/rollout-2026-08-12T11-19-21-019ff4a0-1d4d-79a3-a456-ab7187a79d02.jsonl: thread/resume failed during TUI bootstrap: thread/resume failed: thread 019ff4a0-1d4d-79a3-a456-ab7187a79d02 already has an active writer (code -32600)
What steps can reproduce the bug?
Uploaded thread: 019ff4b6-6708-7830-8f43-11ce2495c89f
What is the expected behavior?
_No response_
Additional information
_No response_
6 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Related reports appear to show the same broader writer-ownership regression in 0.147, but through different transitions:
My reproduction is specifically CLI
/fork: after the TUI moves from parent thread X to forked thread Y,/statusconfirms Y is active, but X still reports an active writer and cannot be resumed in another terminal.These may share the same underlying writer lifecycle / ownership-release mechanism.
I traced the release regression and prepared a draft fix on my fork: https://github.com/noah-foltz/codex/pull/1
Root cause:
/forkswitches to the fork but the oldshutdown_current_threadpath only calledthread/unsubscribe.codex resume <parent>command could still open that rollout concurrently. That appeared to work, but allowed two writers.5c94796dc9e88580fdf0b05ef9ce9d975a86e1a6(#34986) added the-32600guard for paginated histories, first shipped inrust-v0.146.0-alpha.5and stablerust-v0.146.0.dc60dadce6b5911b9b5852365f6eb9829ec14442(#36389) expanded ownership to every history, whiledbcd837c20cd891f3de4bb3976c7f643d0b2fb93(#36950) made normal non-ephemeral TUI histories paginated. Both are present in 0.147.0, making this deterministic for ordinary/forksessions.The proposed fix keeps the single-writer protection. It adds an optional
unload: truemode to v2thread/unsubscribe; app-server reserves teardown, verifies no other connection remains subscribed, shuts down the thread, and waits for the writer lock to be released before responding. Primary TUI thread switches use that mode, so/forkdoes not advertise the parent resume command until ownership has actually been released. Ordinary unsubscriptions retain the existing 30-minute cache.The regression test materializes a rollout in app-server process A, explicitly unloads it, and immediately resumes the same thread in process B. There is also TUI coverage asserting
/forksendsunload: true. Protocol/schema, focused app-server lifecycle, focused TUI lifecycle, and exec suites pass.Current workaround: exit the originating TUI before running the printed parent resume command, or wait for the idle unload timeout.
I independently reproduced this on macOS and captured descriptor-level evidence that the original TUI retains both the parent and child writers after
/fork.Environment
0.147.026.6.1arm64zsh 5.9Reproduction
/fork; the original TUI switches to the child session.codex resume <parent-thread-id>.Descriptor evidence
While the original TUI was actively attached to the child,
lsofshowed that the same original Codex process still held all four resources:The parent rollout's last modification time was
15:08:59; the child rollout was created at15:10:49. The parent JSONL did not change after the fork, but its writable descriptor and writer lock remained open. The last parent JSONL record also parsed successfully, so this was not caused by obvious transcript corruption.This confirms the issue across both WSL (original report) and macOS/arm64: after the TUI switches to the fork, the parent is idle but remains writer-owned by the original process. Exiting the originating TUI releases the ownership and is the current workaround.
The lifecycle explanation in the existing root-cause comment (unsubscribe leaving the parent loaded in the idle-thread cache) is consistent with this observation.
Same mechanism as #38297 (traced there on current
main): the TUI fork/switch path swaps the active thread and never closes/unloads the parent, so the parent stays loaded in the app-server holding its rollout writer — which is exactly thealready has an active writerrejection on cross-terminal resume. Code refs and two fix levels (unload-on-switch-away; release the rollout writer for idle loaded threads): https://github.com/openai/codex/issues/38297#issuecomment-5323038119Worth deduping these two into one tracked fix.
This appears to be a related but distinct variant of this issue. In my case, the lock is retained by a long-running shared
app-serverafter the interactive client that opened the thread is no longer available.Environment
0.149.0Reproduction from the resulting state
The original client-disconnect event was not captured, but the failure is reproducible from the resulting state:
The same failure occurs when selecting the thread through the resume UI.
Actual result
The failure occurs before the resumed TUI becomes usable.
Diagnostic evidence
The writer lock is actively held by the shared app-server, not merely left as an unowned file. Descriptor inspection shows that the same app-server process has both the affected thread's JSONL history and its writer-lock file open. The process also holds writer locks and history files for several older threads.
The affected thread metadata identifies
originator: Codex Desktop,source: vscode, andcli_version: 0.149.0.I also investigated a local session-handoff wrapper. The affected thread and lock predate its installation, and the app-server environment contains no
SESSION_HANDOFF_*variables, so session handoff does not appear to cause this instance.Expected result
Once the original interactive client is gone, a new client should be able to resume the thread. The app-server should release or reclaim writer ownership for disconnected threads, while preserving single-writer protection for genuinely active clients.
This looks like an app-server writer-lifecycle issue related to the
/forkand idle-thread cases already discussed here, but the trigger is client loss / persistence of a shared app-server rather than/fork. The fact that one daemon retains locks for multiple historical threads may help distinguish the cases.