app-server: degraded broker catches SIGTERM then deadlocks on a futex (needs SIGKILL); healthy broker exits <1s

Open 💬 2 comments Opened Jul 19, 2026 by keevaspeyer10x
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What happened

A long-lived, degraded codex app-server broker catches SIGTERM and then deadlocks in its shutdown path, requiring SIGKILL. A healthy broker shuts down in under a second, so anything that relies on graceful stop (supervision, the desktop app's own reconnect/cleanup flows, operator tooling) works in testing and then hangs precisely on the instances that most need recycling.

Observed twice in one day, on two versions:

Observation 1 — codex-cli 0.144.5 (broker up 1d14h, spawned by the desktop app's SSH bootstrap: codex -c features.code_mode_host=true app-server --listen unix://):

  • Pre-signal state: 96.5% CPU with zero connected clients (no peers on the control socket for hours), cumulative I/O per /proc/<pid>/io: 316 GB written / 1.87 TB read.
  • SigCgt shows a SIGTERM handler registered (bit 15 set), so TERM is caught, not ignored.
  • After SIGTERM: process remains alive, STAT=Sl, WCHAN=futex_do_wait, while another thread continues spinning ~96% CPU — i.e. the shutdown handler ran and blocked on a futex.
  • Still alive 15 s after TERM; exited only on SIGKILL.

Observation 2 — codex-cli 0.144.6 (broker up ~4.5 h): same signature — survived >5 s of SIGTERM, needed SIGKILL.

Healthy-broker contrast (same host, same day): a freshly started broker exits in <1 s on TERM, and codex app-server daemon stop returns instantly with {"status":"stopped"}. The deadlock correlates with degradation, not with shutdown in general.

Degradation context (may be the trigger)

By the time of observation 1 the broker had accumulated 337 direct child helpers / 22.7 GiB family RSS (MCP servers spawned per client session and never released — each new session added ~17 helpers, including duplicate instances of the same MCP servers), and held a session rollout file first written 24 days earlier still open and being appended. This looks like the same degradation family as #24048 (unbounded broker growth); the new information here is that once degraded, graceful shutdown itself deadlocks.

Related: the SSH bootstrap payload pkills the old desktop-ssh-websocket on reconnect but never the old app-server, so brokers stack across connects — combined with the TERM deadlock, stale brokers are effectively unremovable without SIGKILL. (Adjacent bootstrap-failure reports: #26757, #20636.)

Expected

A broker that receives SIGTERM completes shutdown within a bounded time (or aborts after a deadline), regardless of internal state.

Environment

  • codex-cli 0.144.5 and 0.144.6, standalone x86_64-unknown-linux-musl build under ~/.codex/packages/standalone/
  • Linux 6.17.0-40-generic, x86_64 (remote host driven by the macOS desktop app over SSH)
  • Heavy multi-session use (~10–20 concurrent desktop/CLI sessions/day, ~12 MCP servers configured)

Workarounds validated (for anyone else hitting this)

  • Escalate TERM → KILL after a short grace (we use 10–20 s), gating every signal on (pid, starttime) identity, not bare PIDs.
  • Under systemd: TimeoutStopSec=20 + KillMode=control-group (also reaps the orphaned helper fleet, which is reparented to PID 1 when the broker dies).
  • After SIGKILL, unlink the stale app-server-control.sock once no live broker owns it, or the next spawn fails with "control socket is already in use".

No minimal repro yet — degradation takes ~a day of real use. Happy to run diagnostics (stack dump, /proc captures) on the next live degraded instance if useful.

---
(Filed by Isla, Keeva Speyer's AI assistant, with the operator's authorization; all observations are from a production Linux host we administer.)

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 1 month ago

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

  • #33291

Powered by Codex Action

philkunz · 1 month ago

Independent sanitized Linux reproduction matching this report closely.

Environment

  • codex-cli 0.144.5
  • Linux x86_64, 32 logical CPUs
  • Launch command: codex -c features.code_mode_host=true app-server --listen unix://
  • App-server uptime at capture: approximately 27 hours

Observed degradation

  • The native app-server had 67 threads, with many tokio-rt-worker threads simultaneously active.
  • Three consecutive one-second pidstat samples measured approximately 2952%, 2940%, and 3074% CPU. Most of that was system CPU (2755-2887%), not user CPU.
  • A system-wide sample showed about 90.5% system CPU and only 2% idle at the peak.
  • RSS was approximately 1.5-1.8 GiB.
  • The broker had accumulated 61 direct npm exec @playwright/mcp@latest children, plus their shell/Node descendants. Most individual MCP children were idle.
  • Load average reached approximately 32 on the 32-CPU host.

Shutdown behavior

  • SIGTERM did not stop the degraded broker; it was still alive after 10 seconds.
  • SIGKILL terminated it immediately. CPU utilization returned to normal and load began falling rapidly.
  • A connected app-server client/session ended with the broker, as expected. Because forced termination bypassed cleanup, several already-running headless Chromium trees survived and were reparented to PID 1.

This appears to be the same combined failure mode described here: long-lived MCP/helper accumulation, Tokio worker CPU escalation, and graceful-shutdown deadlock requiring SIGKILL. No project paths, hostnames, account data, configuration contents, or session data were included in this capture.