Skip to content

WSL2: daemon deletes PID record when ps start time shifts, leaving a running app-server unmanaged #44037

Closed

Description

@LaChimere

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:

  1. Write a temporary PID record using the historical start time.
  2. Run codex app-server daemon version against that temporary state.
  3. The command reports the server as running but deletes the temporary PID record.
  4. 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?

  1. Start a managed app-server in WSL2 using codex app-server daemon start (tested with Codex 0.153.4).
  2. Record the pid and processStartTime from ~/.codex/app-server-daemon/app-server.pid.
  3. After a clock adjustment, compare the recorded time with:
    ps -p <pid> -o lstart=
    In our case, the same running process changed from 08:13:10 to 08:13:03. We have not identified a deterministic trigger for this WSL clock shift.
  4. Run:
    codex app-server daemon version
  5. Observe that the PID file is deleted even though the app-server remains running.
  6. Run:
    codex app-server daemon restart
    It fails with:
    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

  1. added
    CLIIssues related to the Codex CLI
    windows-osIssues related to Codex on Windows systems
    app-serverIssues involving app server protocol or interfaces
    on Sep 9, 2026
  2. github-actions commented on Sep 9, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  3. argszero commented on Sep 9, 2026

    @argszero

    This is the same PID-identity class as #43608 (which I root-caused and posted: macOS pid.rs compares the formatted ps 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 on origin/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):

    match read_process_details(record.pid).await {
        Ok((state, start_time)) => {
            if state.starts_with('Z') { ... return Ok(false); }
            Ok(start_time == record.process_start_time)
        }
        ...
    }

    2. read_process_details reads the human-formatted ps lstart=. (pid.rs:681):

    .args(["-p", &pid.to_string(), "-o", "stat=", "-o", "lstart="])

    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 does fs::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/TZ so both the ps call 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 from 08:13:10 to 08:13:03 (a 7-second wall-clock change), which no locale pin fixes because the underlying string input genuinely differs. So the string-formatted ps lstart= approach is intrinsically fragile for identity validation, independent of locale. Your boot ID + /proc/<pid>/stat start-time-ticks suggestion is the right direction — /proc/<pid>/stat field 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:

    • Do not conflate "process visibility" with "confirmed termination." Already handled in process_matches_record (:537-551) — a kill(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.
    • Avoid destructive side effects on status/version queries. refresh_after_stale_record (pid.rs:300-310) is the chokepoint; it's worth gating the remove_file or 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 lstart to epoch before compare) on both the capture and re-query paths, and keep the query path read-only. This is a clean, self-contained codex-rs bug 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.

  4. hdformat commented on Sep 19, 2026

    @hdformat

    Still reproducible on Codex CLI/app-server 0.155.1 under WSL2.

    I hit the same symptom:

    $ codex app-server daemon restart
    Error: app server is running but is not managed by codex app-server daemon
    

    I killed the old app-server and started a completely fresh managed daemon:

    $ codex app-server daemon start
    {"status":"started","backend":"pid","pid":244084,...,"cliVersion":"0.155.1","appServerVersion":"0.155.1"}
    

    Immediately after startup, ~/.codex/app-server-daemon/app-server.pid existed and contained:

    {
      "pid": 244084,
      "processStartTime": "토  9월 19 09:55:16 2026"
    }

    The same live process was then reported by ps as:

    $ ps -p 244084 -o lstart=
    토  9월 19 09:55:17 2026
    

    More importantly, repeated calls for the same PID showed that lstart continued to shift while the process remained alive:

    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:17 2026
    토  9월 19 09:55:18 2026
    토  9월 19 09:55:18 2026
    토  9월 19 09:55:18 2026
    

    After running codex app-server daemon restart, it failed with the "not managed" error and app-server.pid had been deleted, while PID 244084 was still running.

    So this is still reproducible in 0.155.1, and in this case the ps wall-clock start time visibly changed from 09:55:17 to 09:55:18 for the same live PID.

    This seems to further confirm that wall-clock lstart is not stable enough for process identity validation under WSL2. A monotonic process-start identifier such as /proc/<pid>/stat field 22 (starttime, clock ticks since boot) may avoid the issue.

  5. etraut-openai commented on Sep 19, 2026

    @etraut-openai
    Contributor

    Yes, this change didn't make it into 0.155.1. It should be in 0.156.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions