Repository navigation
macOS desktop: repeated disk writes resource-limit kills from runaway SQLite WAL checkpoint on unbounded logs_2.sqlite; stale SingletonLock then blocks relaunch #32431
Description
TL;DR
On macOS, the desktop app's background codex helper is repeatedly terminated by the OS disk writes resource monitor (jetsam / RLIMIT-style highwater kill), not by an in-app crash. Root cause: the local logging database ~/.codex/logs_2.sqlite has grown to ~2.8 GB and the process spends effectively all of its time in sqlite3_wal_checkpoint_v2 → pwrite, dirtying tens of GB of file-backed memory (worst observed: 34.36 GB of file-backed memory dirtied over 23,191 s, ~1.48 MB/s sustained). macOS counts this against a per-process disk-write highwater limit and kills the process.
Because the kill is unclean, it leaves behind:
- a stale
SingletonLockin~/Library/Application Support/Codex/pointing to a dead PID, and - orphaned helper processes (
browser_crashpad_handler×2,bare-modifier-monitor) reparented to PID 1,
which then prevent the app from relaunching ("nothing happens when I open it") until they are manually cleared.
This is two related defects: (A) the runaway-write termination, and (B) the failure to recover cleanly on next launch.
Environment
| Hardware | Mac16,8 (Apple Silicon, arm64e), 12 CPUs, 24 GB RAM |
| OS | macOS 26.4 (build 25E246) |
| App | ChatGPT desktop (formerly "Codex"), app version 26.707.41301 |
| Framework | Codex Framework.framework version 150.0.7871.115 (previously .101 — see note below) |
| Codesigning ID / Team | codex / 2DC432GLL2 |
| Resource Coalition | com.openai.codex |
Note: crash reports show the bundle path migrating from /Applications/Codex.app/... (≤ Jul 9) to /Applications/ChatGPT.app/... (≥ Jul 10), i.e. a rebrand/update happened mid-window. Both bundles exhibit the identical failure signature.
Defect A — OS terminates the process for excessive disk writes (runaway SQLite WAL checkpoint)
OS-level termination signature
Every one of the last 9 diagnostic reports is a disk writes Microstackshots report (Action taken: none), i.e. the macOS disk-write resource monitor, not a segfault/abort. Most recent report (codex_2026-07-11-065626):
Command: codex
Path: /Applications/ChatGPT.app/Contents/Resources/codex
Parent: UNKNOWN [98060]
PID: 98180
Event: disk writes
Action taken: none
Writes: 34.36 GB of file backed memory dirtied over 23191 seconds
(1481.57 KB per second average),
exceeding limit of 397.68 KB per second over 86400 seconds
Writes limit: 34.36 GB
Free disk space: 6689.84 MB / 460.43 GB, low space threshold 3072 MB
The write path — SQLite WAL checkpoint
The heaviest stack for the terminated process points squarely at SQLite checkpointing (the binary is Rust; symbols are partial but the SQLite frames are clear):
_pthread_start
std::sys::backtrace::__rust_begin_short_backtrace
sqlite3_exec
sqlite3_step
sqlite3_wal_checkpoint_v2
??? (codex)
pwrite ← libsystem_kernel
The write target — unbounded local logging DB
On disk, the logging database is enormous and actively growing while the app runs:
2.6–2.8 GB ~/.codex/logs_2.sqlite (+ ~40 MB logs_2.sqlite-wal, live)
8.7 GB ~/.codex/sessions
271 MB ~/.codex/sqlite
13 GB ~/.codex (total)
A multi-GB SQLite DB under continuous WAL checkpointing memory-maps and re-dirties a large working set on every checkpoint; macOS accounts those dirtied file-backed pages against the process's disk-write highwater limit, so the process is killed on a timer regardless of available RAM. (At the time I write this, free RAM is 81%, yet the process was still killed — this is a write-rate, not a memory-exhaustion, limit.)
Recurrence & escalation (last 9 events)
The macOS monitor re-arms with a doubling highwater after each kill, so the "Writes limit" climbs 2 GB → 8 GB → 34 GB across successive terminations. Free disk space is drawn down in parallel.
| Local time | Command | PID / Parent | Writes limit tripped | Free disk | Bundle |
|---|---|---|---|---|---|
| 2026-07-11 06:56 | codex | 98180 / 98060 | 34.36 GB | 6.69 GB | ChatGPT.app |
| 2026-07-11 00:29 | ChatGPT | 98060 / 1 | 8.59 GB | 42.38 GB | ChatGPT.app |
| 2026-07-10 21:48 | codex | 98180 / 98060 | 2.15 GB | 14.13 GB | ChatGPT.app |
| 2026-07-09 21:14 | ChatGPT | 45816 / 1 | 2.15 GB | 11.46 GB | ChatGPT.app |
| 2026-07-09 07:59 | codex | 42626 / 42316 | 8.59 GB | 15.79 GB | Codex.app |
| 2026-07-09 05:32 | codex | 19673 / 19614 | 2.15 GB | 25.17 GB | Codex.app |
| 2026-07-06 21:36 | codex | 88485 / 88385 | 2.15 GB | — | Codex.app |
| 2026-07-06 19:56 | codex | 88485 / 88385 | 2.15 GB | — | Codex.app |
| 2026-07-05 22:50 | codex | 33712 / 33537 | 2.15 GB | 14.88 GB | Codex.app |
Note the parent/child pairing: the main ChatGPT/Codex process (parent PID 1) and its long-lived codex helper (Contents/Resources/codex) are both killed under the same signature. In the latest event the helper's parent (98060) is exactly the main process that was killed 6.4 h earlier — so the helper kept writing after its parent died.
Defect B — unclean kill blocks relaunch (stale singleton + orphaned helpers)
After the resource-limit kill, the app would not reopen (open -a ChatGPT returns 0 but no process appears). Two leftovers were responsible:
1. Stale Chromium singleton lock in ~/Library/Application Support/Codex/:
SingletonLock -> ZimingWangs-MacBook-Pro-4.local-98060 # PID 98060 no longer exists
SingletonCookie -> 10769481914328928945
SingletonSocket -> /var/folders/.../T/com.openai.codex.fmOZMW/SingletonSocket
98060 was the PID killed at 00:29; the lock was never released, and the new instance would not take over the stale lock.
2. Orphaned helper processes (parent reparented to PID 1) that survived the kill:
PID PPID ELAPSED COMM
17301 1 23:21:47 .../Codex Framework.framework/Versions/150.0.7871.101/Helpers/browser_crashpad_handler
17303 1 23:21:47 .../browser_crashpad_handler
29616 1 03:57:55 .../ChatGPT.app/Contents/Resources/native/bare-modifier-monitor
These handlers persisted for ~23 h after their parent died, still referencing the previous framework version (150.0.7871.101) even though the on-disk framework had already updated to .115.
Manual recovery that worked
# 1. reap the orphaned helpers (all had PPID==1)
kill 17301 17303 29616
# 2. remove the stale singleton artifacts (lock holder PID confirmed dead)
cd ~/Library/Application\ Support/Codex
rm -f SingletonLock SingletonCookie SingletonSocket
# 3. relaunch
open -a ChatGPTThe app then started normally and recreated a healthy SingletonLock -> ...-<live PID>. Full Chromium process tree (main + services + renderers) came up; no new crash report was generated.
Suggested fixes
Defect A (root cause):
- Bound
logs_2.sqlite: enforce a size/row cap with periodic pruning or rotation; a local log DB should not be allowed to reach multiple GB. - Avoid full-WAL-checkpoint storms: use
wal_autocheckpointtuning /PRAGMA journal_size_limit, orTRUNCATE-mode checkpoints on a bounded cadence rather than continuouswal_checkpoint_v2under a huge WAL. - Consider throttling log-write volume at the source; 34 GB dirtied in 6.4 h from a logging path is disproportionate.
- Also bound
~/.codex/sessions(8.7 GB here) with retention/archival limits.
Defect B (resilience):
- On startup, detect and clear a
SingletonLockwhose recorded host matches the local host but whose PID is dead (standard stale-lock recovery), instead of silently failing to launch. - Reap the app's own orphaned helpers (
browser_crashpad_handler,bare-modifier-monitor) on next launch, or make the singleton takeover robust to their presence.
Relationship to existing issues
This report corroborates and sharpens several open issues, but adds the concrete OS disk writes termination signature and the SQLite-WAL write path, which I did not find documented:
- Codex Desktop app-server leaks child processes and writes excessive logs after crash/restart #26869 — "Codex Desktop app-server leaks child processes and writes excessive logs after crash/restart" — closest match; this report supplies the exact write mechanism (SQLite WAL checkpoint on
logs_2.sqlite) and the OS-kill signature behind "excessive logs", plus the relaunch-blocking singleton/orphan detail. - MCP server processes leak: per-thread processes never cleaned up (9+ GB RSS) #30408 — MCP server process leak (9+ GB RSS).
- Codex for macOS accumulates Computer Use / MCP helper processes and unreaped zombie children, causing HID lag and WindowServer/TCC stalls #25744 — macOS helper/zombie accumulation causing HID/WindowServer stalls.
- MCP servers eagerly start per session, causing headed browser MCP processes to accumulate #21984 / Codex Desktop should use a project-scoped MCP process pool instead of starting MCP per session #20883 — MCP-per-session eager start / process-pool proposal.
If maintainers prefer, Defect A can fold into #26869 and Defect B can be filed separately as a launch-resilience bug.
Diagnostic commands (for reproduction / triage)
# OS-kill reports (this is the ground truth — look for "Event: disk writes")
ls -lt /Library/Logs/DiagnosticReports/*[Cc]odex* /Library/Logs/DiagnosticReports/*[Cc]hatGPT*
grep -E '^(Command|Event|Writes:|Writes limit|Parent|Free disk space):' <report>.diag
# write target sizes
du -sh ~/.codex ~/.codex/logs_2.sqlite ~/.codex/sessions
# stale singleton after an unclean kill
ls -la ~/Library/Application\ Support/Codex/Singleton*
# orphaned helpers (PPID==1) left behind
ps -eo pid,ppid,etime,comm | grep -Ei 'Codex Framework|bare-modifier-monitor'Reporter environment captured 2026-07-11. Diagnostic reports available on request (Microstackshots disk writes .diag files, Jul 5–11, 2026).
Activity
github-actions commentedon Jul 11, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
xiaogege6697 commentedon Oct 6, 2026
Same signature on a second machine — adding data points and the outcome after workaround + app update, in case it helps pin down the write loop.
Environment
Defect A data points (this machine)
/Applications/ChatGPT.app/Contents/MacOS/ChatGPT, coalitioncom.openai.codex) hit thedisk writesadvisory twice in one day:write + 8underuv_cancel(Codex Framework, libuv).shutdown_stallevents while the app hung inwrite()— the machine could not shut down cleanly.~/.codex/archived_sessions(4.2 GB, largest single rollout jsonl 1.4 GB) +generated_images(1.3 GB) and VACUUMed the dbs, the write rate dropped an order of magnitude but was still ~7.5 MB/s sustained (8.59 GB over 1,144 s → a third advisory). Notably at that pointthread_history_1.sqlitewas 1.9 GB of live data (VACUUM did not shrink it) andlogs_2.sqlitewas only ~80 MB post-VACUUM — so on this machine the residual churn doesn't look like log-db bloat alone.Defect B confirmed too: found a stale
SingletonLock/SingletonSocket/SingletonCookiein~/Library/Application Support/Codex/pointing at a dead PID; cleared manually.state_5.sqliteshowedbackfill_state = completeandrollout_migration_state = legacy_to_paginated_v1still advancing, for what it's worth.Resolution on this machine: the app auto-updated to 26.930.61225, and with the cleaned data dir + lock removed, a 5-minute launch test shows a stable PID (no relaunch loop), RSS settling at 141–633 MB, and no new advisory. Happy to share the full
.diagpowerstats stacks if useful.xiaogege6697 commentedon Oct 7, 2026
Follow-up with a controlled A/B data point from the 8 GB machine:
imageGenerationrows (1.5 GB of base64 images) fromthread_items(library shrunk 1.9 GB → 411 MB), the app ran for ~12 hours with zero newdisk writesadvisories.write(), Pageins only 300 pages (so not swap thrash).File-level evidence during the burst: mtimes advancing on
logs_2.sqlite,state_5,queue_1,goals_1and their WALs — those four DBs total only ~100 MB on disk, yet the dirty accounting was 2.3 GB in 15 s, i.e. the same pages are being re-dirtied in a loop rather than new data being persisted.thread_history_1.sqliteitself was untouched during the burst.So on this machine the burst correlates with image-bearing context being loaded at launch, and physically manifests as page-level re-dirtying across the small state/log DBs. Happy to run
fs_usagecapture if it would help.xiaogege6697 commentedon Oct 7, 2026
Root cause found on the 8 GB machine — the persistent
disk writesbursts were NOT the log DB.Kernel-level
fs_usage+lsofcapture during a live burst (writes at 160–170 MB/s sustained, one singlewrite()of 224 MB observed, ~18k constant 512 KB writes per 40 s, 4 worker threads each ~1 GB per launch) showed the top writers were:~/.codex/.codex-global-state.jsonhad grown to 214 MB (healthy size: ~33 KB). Every state change re-serializes and rewrites the whole file via the atomictmp + renamepattern, several times per second → that alone accounts for the sustained 160+ MB/s dirty rate. The..codex-global-state.json.tmp-<ts>-<uuid>sidecar files pile up (dozens per launch; 10 stale ones found dating back months).~/.codex/automations/*/automation.toml(a 1.6 KB file) received 1.7 GB of writes in one 40 s window — same write-amplification pattern.Fix verified on this machine: back up the file, reset it to
{}, launch the app — it rebuilds at 33 KB, first-launch real-time disk writes drop to <1 MB/s (vs 160–170 MB/s), zero new tmp files in 35 s.If others want to check:
ls -lh ~/.codex/.codex-global-state.json— if it's tens/hundreds of MB, this unbounded growth ofelectron-persisted-atom-stateand its full-file rewrite per change is very likely your advisory source too.