Linux: retiring runtime thread kills a still-running sandboxed command
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of Codex CLI is running?
codex-cli 0.155.0
What subscription do you have?
ChatGPT Pro 20x
Which model were you using?
gpt-6-astra max
What platform is your computer?
Linux 6.17.0-1032-oem x86_64 x86_64
What terminal emulator and version are you using (if applicable)?
GNOME Terminal 3.52.0 (Ubuntu 24.04)
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1789847452s since unix epoch",
"overallStatus": "ok",
"codexVersion": "0.155.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/home/anon/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/home/anon/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/home/anon/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/home/anon/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/home/anon/.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/anon/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "true",
"stored ChatGPT tokens": "false",
"stored agent identity": "false",
"stored auth mode": "api_key"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/home/anon/.codex",
"active thread overrides": "not inspected",
"config.toml": "/home/anon/.codex/config.toml",
"config.toml parse": "ok",
"configuration load ms": "3",
"configuration scope": "invocation config, including cloud-managed policy",
"cwd": "/home/anon",
"enabled feature flags": "shell_tool, view_image, sleep_tool, unified_exec, unified_exec_tty, unified_exec_zsh_fork, shell_snapshot, content_item_kinds, code_mode_host, terminal_resize_reflow, sqlite, hooks, enable_request_compression, unbounded_connection_retries, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, in_app_chat, in_app_dictation, in_app_local_automation, 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, compaction_image_budget, workspace_dependencies",
"feature flag overrides": "none",
"feature flags enabled": "47",
"log dir": "/home/anon/.codex/log",
"mcp servers": "2",
"model": "gpt-6-astra",
"model provider": "openai",
"sqlite home": "/home/anon/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git executable found; execution not verified",
"details": {
"PATH git #1": "/usr/bin/git",
"PATH git #2": "/bin/git",
"PATH git entries": "2",
"git execution": "not inspected (PATH helpers are not executed)",
"repo detected": "false",
"selected git": "/usr/bin/git"
},
"remediation": null,
"durationMs": 0
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/home/anon/.local/bin/codex",
"current executable": "/home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/bin/codex",
"install context": "standalone (unix, package /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl, bin /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/bin, resources /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/codex-resources, path /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/codex-path)",
"managed by Vite+": "false",
"managed by bun": "false",
"managed by npm": "false",
"managed by pnpm": "false",
"managed package root": "not set"
},
"remediation": null,
"durationMs": 0
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "2",
"disabled servers": "0",
"stdio servers": "1",
"streamable_http servers": "1"
},
"remediation": null,
"durationMs": 98
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"managed proxy": "not configured",
"proxy env vars": "none",
"respect system proxy": "disabled"
},
"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 inference URL": "https://api.openai.com/v1/<redacted> reachable (HTTP 401)",
"openai API route probe": "https://api.openai.com/v1/<redacted> route exists (HTTP 401)",
"reachability mode": "API key auth"
},
"remediation": null,
"durationMs": 389
},
"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": "api_key",
"connect timeout": "15000 ms",
"endpoint": "wss://api.openai.com/v1/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 820
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on linux-x86_64",
"details": {
"commit": "unknown",
"current executable": "/home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/bin/codex",
"install method": "standalone (unix, package /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl, bin /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/bin, resources /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/codex-resources, path /home/anon/.codex/packages/standalone/releases/0.155.0-x86_64-unknown-linux-musl/codex-path)",
"platform": "linux-x86_64",
"version": "0.155.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search command found (bundled); execution not verified",
"details": {
"search command": "/home/anon/.codex/packages/standalone/releases/0.155.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/anon/.codex/tmp/arg0/codex-arg0zmahj9/codex-linux-sandbox",
"denied-read glob rules": "0",
"denied-read rules": "0",
"execve wrapper helper": "/home/anon/.codex/tmp/arg0/codex-arg0zmahj9/codex-execve-wrapper",
"filesystem sandbox": "restricted",
"glob scan max depth": "unbounded",
"managed filesystem source": "none",
"network sandbox": "restricted"
},
"remediation": null,
"durationMs": 0
},
"security.endpoint": {
"id": "security.endpoint",
"category": "security",
"status": "ok",
"summary": "endpoint protection is not inspected on this platform",
"details": {
"endpoint products": "not inspected on this platform"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/home/anon/.codex (dir)",
"active rollout files": "330 files, 2821248130 total bytes, 8549236 average bytes",
"archived rollout files": "0 files, 0 total bytes, 0 average bytes",
"goals DB": "/home/anon/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "/home/anon/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/home/anon/.codex/log (dir)",
"memories DB": "/home/anon/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"memories v2 DB": "/home/anon/.codex/memories_v2_1.sqlite (missing)",
"memories v2 DB integrity": "skipped (missing)",
"queue DB": "/home/anon/.codex/queue_1.sqlite (file)",
"queue DB integrity": "ok",
"sqlite home": "/home/anon/.codex (dir)",
"standalone release cache": "7 entries in /home/anon/.codex/packages/standalone/releases",
"state DB": "/home/anon/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "/home/anon/.codex/thread_history_1.sqlite (file)",
"thread history DB integrity": "ok"
},
"remediation": null,
"durationMs": 1962
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "ok",
"summary": "rollout files and state DB thread inventory agree",
"details": {
"default model provider": "openai",
"rollout DB active files": "330",
"rollout DB active rows": "330",
"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": "0",
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=330",
"rollout DB rows": "330",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "subagent:thread_spawn=303, cli=23, vscode=4",
"rollout DB stale rows": "0"
},
"remediation": null,
"durationMs": 112
},
"system.disk": {
"id": "system.disk",
"category": "disk",
"status": "ok",
"summary": "sufficient free disk space (3067.5 GiB)",
"details": {
"CODEX_HOME available": "3067.5 GiB",
"failure threshold": "1.0 GiB",
"warning threshold": "5.0 GiB",
"worktree available": "3067.5 GiB"
},
"remediation": null,
"durationMs": 0
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language en-US",
"details": {
"EDITOR": "not set",
"LANG": "en_US.UTF-8",
"VISUAL": "not set",
"os": "Ubuntu 24.4.0 (noble) [64-bit]",
"os language": "en-US",
"os type": "Ubuntu",
"os version": "24.4.0"
},
"remediation": null,
"durationMs": 5
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"DISPLAY": "present",
"WAYLAND_DISPLAY": "present",
"color output": "enabled",
"effective locale": "en_US.UTF-8",
"stderr is terminal": "true",
"stdin is terminal": "true",
"stdout is terminal": "true",
"terminal": "GNOME Terminal",
"terminal size": "80x24"
},
"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": "anon",
"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.155.1",
"check for update on startup": "true",
"last checked at": "2026-09-19T13:22:05.939863078Z",
"latest version": "0.155.1",
"latest version status": "newer version is available",
"update action": "standalone installer",
"version cache": "/home/anon/.codex/version.json"
},
"remediation": null,
"durationMs": 504
}
}
}
What issue are you seeing?
Sandboxed commands terminate unexpectedly while the Codex CLI process remains alive, without me cancelling the command or exiting Codex. This was captured three times with Codex CLI
0.155.0, the official Linux x86_64 musl release, on Ubuntu 24.04.
The workload uses Codex’s managed network proxy. It initially works, then the application reports:
RuntimeError: ModelArk connection failed: [Errno 111] Connection refused
The command returns exit code 143.
Syscall tracing established this sequence:
- A Codex OS thread creates a sandbox helper, which successfully arms PR_SET_PDEATHSIG(SIGTERM).
- That creating thread later exits while the parent Codex process remains alive.
- The helper immediately receives SIGTERM.
- Its proxy bridge dies, and subsequent connections to the local proxy listener fail with ECONNREFUSED.
For the third incident, on 18 September 2026:
- Codex process PID: 538183
- Creating OS thread TID: 538194
- Sandbox helper PID: 543688
- Proxy bridge PID: 543689
- Creating thread exited: 15:52:18.202179 UTC
- Helper received SIGTERM: 191 microseconds later
- Refused local endpoint: 127.0.0.1:37501
The helper had been running for about 27 minutes. The other two captures show the same sequence, with delays of 480 and 181 microseconds.
No captured kill/tkill/tgkill initiated the first termination. Later signal forwarding and cleanup happened after it. The parent CLI remained alive in all three cases.
The refusal was at the local proxy, before any provider authentication or rate-limit response.
Attached: live-incidents.json, containing selected syscall records, thread ancestry, the application error excerpt, and trace-snapshot hashes. Private application paths, credentials,
and request payloads are omitted.
What steps can reproduce the bug?
The original full CLI workload trigger is intermittent, but the underlying Linux thread-lifetime mechanism has a standalone reproduction.
Download the attached reproduce-parent-thread.sh and run:
bash reproduce-parent-thread.sh
Requirements: Linux, a Rust toolchain supporting edition 2024, and a native linker. Verified with Rust 1.95.0 on GNU/Linux.
The script contains the complete Rust source, Cargo manifest, lockfile, and source attribution. It creates a fresh temporary project, builds it, and runs two modes. Cargo may
download the pinned dependencies; the fixture itself requires no network, Codex installation, model, or API credentials.
It uses Tokio 1.52.3, libc crate 0.2.186, and the exact set_parent_death_signal helper from Codex 0.155.0.
Reproduction sequence:
- Create a twelve-second sleeping child from a Tokio runtime worker, arming PR_SET_PDEATHSIG(SIGTERM) in pre_exec.
- Control mode: leave the worker in place. The child completes normally after about twelve seconds.
- Handoff mode: use tokio::task::block_in_place and confirm that a replacement OS thread takes over. Keep the parent process and runtime alive.
- The original thread retires after Tokio’s normal ten-second idle blocking-thread lifetime. Linux sends SIGTERM to its child before the child completes.
Fresh results:
- Control: child exited normally after 12,010 ms.
- Handoff: child received SIGTERM after 10,120 ms, with its parent still alive and its creating thread gone.
Both modes print JSON containing PIDs, the creating thread TID, thread-alive checks, child status, and elapsed time.
The harness itself returns success when it observes the expected results, including the bug in handoff mode. Its JSON signal and exit_code fields describe the child.
This demonstrates the lifetime mechanism. The exact live call stack that initiated the earlier CLI handoffs was not captured. The fixture uses GNU/glibc; the original incidents were
captured with the official musl binary.
Attached: reproduce-parent-thread.sh and standalone-repro-results.json.
What is the expected behavior?
A still-running command and its sandbox helpers should survive normal retirement of an internal Codex runtime worker. In the reproduction, the twelve-second child should complete
with exit code 0 even after the requesting thread exits.
The command should still be cleaned up when explicitly cancelled or when the owning Codex process actually dies. Fixing the lifetime relationship should preserve filesystem/network
restrictions and normal cancellation behavior.
Additional information
The Linux spawn path installs PR_SET_PDEATHSIG(SIGTERM) in pre_exec and spawns directly from the requesting runtime thread. Linux ties that signal to the lifetime of the creating OS
thread, even if the rest of the parent process remains alive.
Relevant Codex 0.155.0 source:
- codex-rs/utils/pty/src/pipe.rs
- codex-rs/utils/pty/src/process_group.rs
- codex-rs/core/src/spawn.rs
A local candidate fix routes Linux process creation through a persistent OS thread, entering the requesting Tokio runtime for each spawn. The pipe and core shell paths share it. Only
process creation is serialized; commands still run concurrently. The parent-death guard, process-group setup, sandbox restrictions, and cancellation behavior are retained.
A controlled comparison using the actual patched codex-utils-pty crate, with the same Rust 1.95.0/GNU build for both paths, produced:
- Original direct spawn: requesting thread exited, parent remained alive, child received SIGTERM after 10,101 ms.
- Persistent spawning thread: requesting thread exited, parent remained alive, child completed normally after 12,002 ms.
The changed PTY crate passed 31 tests, including checks for surviving requesting-thread exit and retaining cleanup when the whole parent process is killed. Scoped lint and formatting
passed. The broader repository test suite was not fully passing, so this is focused validation rather than a claim of complete release readiness.
The patched CLI also ran the affected workflow for hours without recurrence. That CLI build uses glibc, unlike the official musl release; the controlled comparison above holds libc
constant.
Source inspection on 19 September 2026 found the relevant three files unchanged in 0.155.1 (be2951ea34f0d295ed0becf97079f92fa5f6950e), and the same Linux lifetime relationship
present in main at 595cc91e8cbb1c2ca822d0311dcf12709410c582. Those newer revisions were checked in source; a runtime reproduction on them is not claimed.
Issue #45419 may be related, but it lacks thread/process evidence to confirm a shared cause.
Separate earlier DNS/private-address-policy errors remain unresolved. Those diagnostics do not occur in these three captures, and this report does not claim that the local lifetime
fix resolves them.
Attached: patched-comparison.json.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No source file, test, reproduction command, or entry point is named in the report. Start by reproducing the Linux sandbox failure and tracing the retiring runtime thread's handling of a still-running command. Done means the command remains alive when that runtime thread retires, with a regression test covering the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, rust
- Domain
- operating-systems, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 42/100