Repository navigation
WSL2: daemon deletes PID record when ps start time shifts, leaving a running app-server unmanaged #44037
Description
What version of Codex CLI is running?
0.153.4
What subscription do you have?
Plus
Which model were you using?
No response
What platform is your computer?
WSL2 Ubuntu 26.04 (Kernel 6.18.33.2-microsoft-standard-WSL2)
What terminal emulator and version are you using (if applicable)?
WSL2
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1788925135s since unix epoch",
"overallStatus": "fail",
"codexVersion": "0.153.4",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is running",
"details": {
"app-server version": "0.153.4",
"control socket": "<HOME>/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "<HOME>/.codex/app-server-daemon",
"mode": "persistent",
"pid file": "<HOME>/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "<HOME>/.codex/app-server-daemon/settings.json (file)",
"status": "running",
"update-loop pid file": "<HOME>/.codex/app-server-daemon/app-server-updater.pid (file)"
},
"remediation": null,
"durationMs": 109
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "<HOME>/.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>/.codex",
"config.toml": "<HOME>/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "<HOME>/.codex",
"enabled feature flags": "shell_tool, view_image, sleep_tool, unified_exec, 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, remote_compaction_v2, compaction_image_budget, workspace_dependencies",
"feature flag overrides": "none",
"feature flags enabled": "47",
"log dir": "<HOME>/.codex/log",
"mcp servers": "0",
"model": "gpt-6-astra",
"model provider": "copilot_api",
"sqlite home": "<HOME>/.codex"
},
"remediation": null,
"durationMs": 1
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.53.0",
"details": {
"PATH git #1": "/usr/bin/git",
"PATH git #2": "/bin/git",
"PATH git entries": "2",
"git build options": "git version 2.53.0; cpu: x86_64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh; rust: disabled; gettext: enabled; libcurl: 8.18.0; zlib: 1.3.1; SHA-1: SHA1_DC; SHA-256: SHA256_BLK; default-ref-format: files; default-hash: sha1",
"git exec path": "/usr/lib/git-core",
"git version": "git version 2.53.0",
"repo detected": "false",
"selected git": "/usr/bin/git"
},
"remediation": null,
"durationMs": 265
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "<HOME>/.local/bin/codex",
"current executable": "<HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/bin/codex",
"install context": "standalone (unix, package <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl, bin <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/bin, resources <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/codex-resources, path <HOME>/.codex/packages/standalone/releases/0.153.4-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": 270
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "no MCP servers configured",
"details": {},
"remediation": null,
"durationMs": 0
},
"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": {
"copilot_api API inference URL": "http://127.0.0.1:4141/v1/<redacted> reachable (HTTP 401)",
"copilot_api API route probe": "http://127.0.0.1:4141/v1/<redacted> route exists (HTTP 401)",
"reachability mode": "API key auth"
},
"remediation": null,
"durationMs": 90
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket is not enabled for the active provider",
"details": {
"model provider": "copilot_api",
"provider name": "OpenAI",
"proxy env vars": "none",
"supports websockets": "false",
"wire API": "responses"
},
"remediation": null,
"durationMs": 0
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on linux-x86_64",
"details": {
"commit": "unknown",
"current executable": "<HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/bin/codex",
"install method": "standalone (unix, package <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl, bin <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/bin, resources <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/codex-resources, path <HOME>/.codex/packages/standalone/releases/0.153.4-x86_64-unknown-linux-musl/codex-path)",
"platform": "linux-x86_64",
"version": "0.153.4"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "<HOME>/.codex/packages/standalone/releases/0.153.4-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>/.codex/tmp/arg0/codex-arg0b4XKdH/codex-linux-sandbox",
"execve wrapper helper": "<HOME>/.codex/tmp/arg0/codex-arg0b4XKdH/codex-execve-wrapper",
"filesystem sandbox": "restricted",
"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>/.codex (dir)",
"active rollout files": "83 files, 230282716 total bytes, 2774490 average bytes",
"archived rollout files": "49 files, 200679265 total bytes, 4095495 average bytes",
"goals DB": "<HOME>/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "<HOME>/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "<HOME>/.codex/log (dir)",
"memories DB": "<HOME>/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"queue DB": "<HOME>/.codex/queue_1.sqlite (file)",
"queue DB integrity": "ok",
"sqlite home": "<HOME>/.codex (dir)",
"standalone release cache": "5 entries in <HOME>/.codex/packages/standalone/releases",
"state DB": "<HOME>/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "<HOME>/.codex/thread_history_1.sqlite (file)",
"thread history DB integrity": "ok"
},
"remediation": null,
"durationMs": 8852
},
"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": "copilot_api",
"rollout DB active files": "83",
"rollout DB active rows": "83",
"rollout DB archive mismatches": "0",
"rollout DB archived files": "49",
"rollout DB archived rows": "49",
"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": "copilot_api=132",
"rollout DB rows": "132",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "subagent:other=46, subagent:thread_spawn=31, vscode=24, cli=21, exec=10",
"rollout DB stale rows": "0"
},
"remediation": null,
"durationMs": 260
},
"system.disk": {
"id": "system.disk",
"category": "disk",
"status": "ok",
"summary": "sufficient free disk space (616.0 GiB)",
"details": {
"CODEX_HOME available": "616.0 GiB",
"failure threshold": "1.0 GiB",
"warning threshold": "5.0 GiB",
"worktree available": "616.0 GiB"
},
"remediation": null,
"durationMs": 0
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language C",
"details": {
"EDITOR": "not set",
"GH_PAGER": "set",
"GIT_PAGER": "set",
"LANG": "C.UTF-8",
"LC_ALL": "C.UTF-8",
"LC_CTYPE": "C.UTF-8",
"LESS": "set",
"PAGER": "set",
"VISUAL": "not set",
"os": "Ubuntu 26.4.0 (resolute) [64-bit]",
"os language": "C",
"os type": "Ubuntu",
"os version": "26.4.0"
},
"remediation": null,
"durationMs": 34
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "fail",
"summary": "TERM=dumb - colors and cursor control are disabled",
"details": {
"COLORTERM": "present",
"NO_COLOR": "1",
"TERM": "dumb",
"color output": "disabled (NO_COLOR)",
"effective locale": "C.UTF-8",
"stderr is terminal": "false",
"stdin is terminal": "false",
"stdout is terminal": "false",
"terminal": "dumb",
"terminal size": "80x24"
},
"issues": [
{
"severity": "fail",
"cause": "TERM=dumb - colors and cursor control are disabled",
"measured": "TERM=dumb",
"expected": "TERM=xterm-256color or another real terminal type",
"remedy": "set TERM to a real value, for example xterm-256color",
"fields": [
"TERM"
]
}
],
"remediation": null,
"durationMs": 6
},
"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": ".codex",
"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.153.4",
"check for update on startup": "true",
"last checked at": "2026-09-09T02:47:57.497948060Z",
"latest version": "0.153.4",
"latest version status": "current version is not older",
"update action": "standalone installer",
"version cache": "<HOME>/.codex/version.json"
},
"remediation": null,
"durationMs": 64
}
}
}What issue are you seeing?
Environment
- Codex CLI and app-server: 0.153.4
- WSL2 kernel: 6.18.33.2-microsoft-standard-WSL2
- ps: procps-ng 4.0.4
Problem
I repeatedly encounter:
Error: app server is running but is not managed by codex app-server daemon
The app-server remains running and reachable, but ~/.codex/app-server-daemon/app-server.pid disappears.
Evidence
For the same running process, PID 1293355, the recorded and subsequently reported start times differed:
Previously saved processStartTime: Tue Sep 8 08:13:10 2026
Later ps -p 1293355 -o lstart=: Tue Sep 8 08:13:03 2026
Codex compares these strings to validate process identity. A mismatch causes it to classify the record as stale and delete the PID file, even though the server is still running.
I reproduced this using the installed Codex binary, temporary daemon state directories, and the existing server’s control socket:
- Write a temporary PID record using the historical start time.
- Run
codex app-server daemon versionagainst that temporary state. - The command reports the server as running but deletes the temporary PID record.
- Repeat using the current start time: the record remains, and the response includes
"backend":"pid".
The live server remained running throughout. This also demonstrates that a seemingly informational daemon version command can remove management metadata.
Expected behavior
A change in the formatted wall-clock start time should not cause a live daemon to lose its management record. Status queries should ideally avoid destructive metadata cleanup.
Possible cause and fix
The observed seven-second shift is consistent with a clock adjustment in WSL2, but we do not have logs identifying the specific clock event.
On Linux, consider identifying processes using boot ID plus /proc/<pid>/stat start-time ticks instead of comparing formatted ps timestamps. Process visibility failures should also be distinguished from confirmed process termination.
Relevant code: PID identity validation and stale-record cleanup.
Workaround
I switched to a custom systemd user service that directly runs:
codex app-server --remote-control --listen unix://
This avoids relying on Codex daemon’s PID tracking.
What steps can reproduce the bug?
- Start a managed app-server in WSL2 using
codex app-server daemon start(tested with Codex0.153.4). - Record the
pidandprocessStartTimefrom~/.codex/app-server-daemon/app-server.pid. - After a clock adjustment, compare the recorded time with:
In our case, the same running process changed from
ps -p <pid> -o lstart=
08:13:10to08:13:03. We have not identified a deterministic trigger for this WSL clock shift. - Run:
codex app-server daemon version
- Observe that the PID file is deleted even though the app-server remains running.
- Run:
It fails with:
codex app-server daemon restart
Error: app server is running but is not managed by codex app-server daemon
Deterministic reproduction of the faulty validation: We also tested with temporary Codex state directories pointing to the existing server’s control socket. A PID record containing the historical start time was deleted by daemon version; an otherwise identical record containing the current ps start time was preserved and returned "backend":"pid". The live server remained running throughout.
What is the expected behavior?
No response
Additional information
No response
Activity
github-actions commentedon Sep 9, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
argszero commentedon Sep 9, 2026
This is the same PID-identity class as #43608 (which I root-caused and posted: macOS
pid.rscompares the formattedps lstart=string, and it becomes locale/locale-dependent), but your WSL2 report is stronger and reveals a deeper fragility — so worth treating as the more general case. Verified current onorigin/main(codex-rs/app-server-daemon/src/backend/pid.rs):1. Identity validation is a string equality on the formatted wall-clock value. The unix
process_matches_record(pid.rs:537):2.
read_process_detailsreads the human-formattedps lstart=. (pid.rs:681):so the comparison is on
ps's formatted wall-clock string, not an epoch/ticks value.3. Stale-cleanup deletes the PID file on mismatch. Failed matching routes to
refresh_after_stale_record(pid.rs:300), which doesfs::remove_file(&self.pid_file)when the record no longer matches — turning what should be a read-only status query (daemon version) into destructive metadata removal for a still-running daemon. That's the "Status queries should ideally avoid destructive metadata cleanup" concern you flagged.Why your case is broader than #43608. My #43608 fix was to pin
LC_ALL=C/TZso both thepscall from the capture path and the re-query run under the same locale — that handles a format mismatch. But you've shown the value itself can change: a WSL2 clock adjustment shifted the same running process from08:13:10to08:13:03(a 7-second wall-clock change), which no locale pin fixes because the underlying string input genuinely differs. So the string-formattedps lstart=approach is intrinsically fragile for identity validation, independent of locale. Yourboot ID+/proc/<pid>/statstart-time-ticks suggestion is the right direction —/proc/<pid>/statfield 22 is an OS-boot-anchored ticks value that is stable across NTP/clock adjustments and locale, and the boot id (/proc/sys/kernel/random/boot_id) disambiguates PID reuse across boots. Two notes to fold in:process_matches_record(:537-551) — akill(pid,0)success but non-matching start time should read as "unknown/ambiguous," not "stale." Your repro (live server + deleted PID) is precisely the case where the process is demonstrably alive yet gets classified stale.refresh_after_stale_record(pid.rs:300-310) is the chokepoint; it's worth gating theremove_fileor splitting "queryable but not matched" from "confirmed gone."I'd suggest consolidating #43608 + this into one fix that moves to a locale/clock-robust identity source (boot-id + proc stat ticks, or normalize
lstartto epoch before compare) on both the capture and re-query paths, and keep the query path read-only. This is a clean, self-containedcodex-rsbug that I can implement as a fork PR if you'd like a hand. Cross-linking so the maintainers see the macOS + WSL2 picture together.hdformat commentedon Sep 19, 2026
Still reproducible on Codex CLI/app-server 0.155.1 under WSL2.
I hit the same symptom:
I killed the old app-server and started a completely fresh managed daemon:
Immediately after startup,
~/.codex/app-server-daemon/app-server.pidexisted and contained:{ "pid": 244084, "processStartTime": "토 9월 19 09:55:16 2026" }The same live process was then reported by
psas:More importantly, repeated calls for the same PID showed that
lstartcontinued to shift while the process remained alive:After running
codex app-server daemon restart, it failed with the "not managed" error andapp-server.pidhad been deleted, while PID 244084 was still running.So this is still reproducible in 0.155.1, and in this case the
pswall-clock start time visibly changed from09:55:17to09:55:18for the same live PID.This seems to further confirm that wall-clock
lstartis not stable enough for process identity validation under WSL2. A monotonic process-start identifier such as/proc/<pid>/statfield 22 (starttime, clock ticks since boot) may avoid the issue.etraut-openai commentedon Sep 19, 2026
Yes, this change didn't make it into 0.155.1. It should be in 0.156.0.