
Codex CLI 疯狂写盘自救指南
周言志Codex CLI 疯狂写盘自救指南
最近 OpenAI Codex CLI 被曝出一个相当严重的磁盘写入 bug:SQLite 日志在 TRACE 级别全量落盘,一年能写穿一块消费级 NVMe SSD。本文带你理解问题原理、检查自己是否中招,并提供从升级到触发器的完整自救方案。
这个 bug 到底在干什么
Codex CLI 会把运行日志写进一个本地 SQLite 数据库 ~/.codex/logs_2.sqlite。问题出在它在 TRACE 级别全量落盘——每一个 WebSocket 事件、每一条依赖库的噪声日志都往里写,而且不停地 insert + prune,造成严重的写放大。
社区实测(issue #28224):
- 一台机器连续跑 21 天,向磁盘写了约 37 TB,折算下来一年约 640 TB。
- 消费级 NVMe SSD 的标称写入寿命(TBW)通常只有 600 TB 左右——理论上不到一年就能把一块盘的寿命写穿。
- 还带两个连锁故障:
logs_2.sqlite超过约 200MB 时 CLI 会以 SIGTRAP 崩溃(#29237);logs_2.sqlite-wal能无上限涨到几十 GB(#28997)。
0.142.0(2026-06-22 发布)带来了部分修复:两个 PR——#29432(停掉逐条 WebSocket 事件日志)和 #29457(过滤噪声 target)——按 issue 估算能砍掉约 85% 的日志写入,纯命令行场景改善明显。
但它没有完全修好。 实测在 macOS + Codex 桌面 App(app-server) 路径下,即使内嵌 CLI 已经是 0.142.0,
TRACE target=log的高频写入依然存在——60 秒采样里max(id)还在前进 1400~1700 行,多人复现(#29532,OPEN)。所以本篇不只教你升级,还给你可靠检测 + 清理 + 在线止血的整套兜底。
还有一点:升级只能止住未来的写入,已经被写爆的旧日志不会自动回收。完整处置流程是 升级 → 真正确认停没停 → 没停就清理 / trigger 止血。
一、确认自己中没中招
先看日志文件有多大,重点盯 -wal 那个:
1 | ls -lah ~/.codex/logs_2.sqlite* |
如果 logs_2.sqlite-wal 已经是几百 MB 甚至 GB 级,基本可以确定你正被这个 bug 持续写盘。
光看文件大小会骗你。 insert + prune 的 churn 会让
-wal稳定在几 MB、logs_2.sqlite体积也不怎么涨,看着没事,底层却一直在写(#29532 实测)。更靠谱的判定是隔 60 秒采两次行号,看它还涨不涨:
1
2 sqlite3 ~/.codex/logs_2.sqlite "SELECT MAX(id) FROM logs;"
# 等 60 秒再跑一次;数值仍在大幅前进(几百上千)= 正在持续写盘
Windows 路径是 %USERPROFILE%\.codex\,直接在资源管理器里看这三个 logs_2.sqlite* 文件的大小即可。
二、升级到 0.142.0
两种方式任选其一。
方式一:npm 手动升级
1 | npm install -g @openai/codex@latest |
@latest 当前解析到的就是 0.142.0,已经包含 #29432 / #29457 这两个修复。
升级完确认版本:
1 | codex --version |
方式二:让 AI 代办
如果你装了 Claude Code,把这句话丢给它,让它联网搜索并完成升级:
1 | search web & update codex cli |
必须是
0.142.0或更高才带修复。0.141.x及更早版本仍会疯狂写盘——重装同一个旧版本没用,认准版本号。升级后务必回第一步用max(id)采样复测,因为 macOS App 路径上它可能还没停(见下文)。
三、清理已经被写爆的旧日志
动手前先完全退出 Codex(命令行会话和桌面 App 都关掉)。文件还被占用时删不干净,WAL 也可能立刻重建。
这个日志库不含任何对话内容或配置,纯粹是诊断日志,删掉是安全的——Codex 下次启动会自动重建一个干净的小文件。
macOS / Linux:
1 | rm ~/.codex/logs_2.sqlite ~/.codex/logs_2.sqlite-wal ~/.codex/logs_2.sqlite-shm |
或者一行通配搞定:
1 | rm ~/.codex/logs_2.sqlite* |
Windows(PowerShell):
1 | Remove-Item "$env:USERPROFILE\.codex\logs_2.sqlite*" |
四、验证修复生效
- 版本号到位:
1 | codex --version |
- 别只看文件大小,按第一步那招采样
max(id),确认写入真的停了:
1 | sqlite3 ~/.codex/logs_2.sqlite "SELECT MAX(id) FROM logs;" |
3.(进阶,Linux)直接看 SSD 实际写入量。用 smartctl 对比挂机前后的 Data Units Written:
1 | sudo smartctl -a /dev/nvme0 | grep "Data Units Written" |
挂机一小时前后做差值,正常情况下增量应该很小,而不是之前那种几 GB/小时的量级。
如果你用的是 macOS + Codex 桌面 App,升到 0.142.0 后
max(id)很可能仍在涨——这是已知的部分修复缺口(#29532)。这时别干等,直接上「进阶」里的 trigger 在线止血。
五、进阶:用 SQLite Trigger 在线止血
如果你不想删库、又想立刻止住写盘(尤其是升级到 0.142.0 后、在 macOS App 上仍没停的情况),可以用 SQLite trigger 拦截 logs 表的 insert。日志全落在一张名为 logs 的表里(主键 id),所以能精确处理。这也正是 0.142.0 之后社区在 macOS App 上仍在用的兜底方案(#29532 评论里多人在用同名 block_log_inserts trigger)。
1. 确诊:TRACE 是不是元凶
1 | sqlite3 ~/.codex/logs_2.sqlite "SELECT level, COUNT(*) FROM logs GROUP BY level ORDER BY COUNT(*) DESC;" |
实测里 TRACE 能占到五到七成行数,大头是 target=log 的 websocket / tungstenite 噪声。再隔 30 秒采两次 MAX(id),看还在不在涨:
1 | sqlite3 ~/.codex/logs_2.sqlite "SELECT MAX(id) FROM logs;" |
id 一次响应能跳几千、或每分钟稳定涨一两千,就是正在被持续写盘。
别想着用
RUST_LOG=info来压——实测设了它,TRACE/DEBUG照样落进 SQLite(#29532)。这条持久化路径不吃环境变量的过滤。
2. 先备份
即使 Codex 在跑也能做一致性备份:
1 | sqlite3 ~/.codex/logs_2.sqlite ".backup '$HOME/.codex/logs_2.sqlite.bak'" |
3. 用 trigger 拦住 logs 表的 insert
拦普通表的 insert 要用
BEFORE INSERT+RAISE(IGNORE),不是INSTEAD OF(那个只能建在视图上,照抄会报错)。
1 | sqlite3 ~/.codex/logs_2.sqlite "CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;" |
之后所有往 logs 表的写入都会被静默忽略(不报错),从源头止住。trigger 是库级的,对 Codex 已经打开的连接也会即时生效。
4. Checkpoint 并截断 WAL,把空间收回来
1 | sqlite3 ~/.codex/logs_2.sqlite "PRAGMA wal_checkpoint(TRUNCATE);" |
返回第一列是 0 表示截断成功;如果是 1(busy),说明 Codex 还占着连接,彻底退出 Codex 再跑一次。
5. 采样确认彻底不涨了
1 | sqlite3 ~/.codex/logs_2.sqlite "SELECT MAX(id) FROM logs;" # 隔 30 秒两次,数值应不变 |
MAX(id) 钉死不动、-wal 不再变大,就说明止血成功。
这是侵入式的临时手段,升级到 0.142.0 仍是第一步(它确实砍掉了一大块)。等官方把 macOS App 的
target=log缺口也补上后,记得把 trigger 删掉还原,否则诊断日志会一直写不进去:
1 sqlite3 ~/.codex/logs_2.sqlite "DROP TRIGGER IF EXISTS block_log_inserts;"另外 trigger 只对
logs表下手,别对state_5.sqlite等状态库这么干。
让 AI 代跑整套流程
如果你用的是 Claude Code 或 Codex 自己,把这段丢给它,让它代跑上面的步骤:
1 | 排查 ~/.codex/logs_2.sqlite 是否因 TRACE 日志持续高频写盘: |
桌面 App 用户特别注意事项
如果你用的是 Codex 桌面 App 而非命令行,注意以下几点:
- App 走独立版本线(最新
26.616,2026-06-18 发布,早于 CLI 的 0.142.0 修复),内嵌自己管理的引擎——所以npm升级 CLI 不一定能让 App 用上这个修复。 - 用完彻底退出 App(不是最小化)。App 挂后台跑长任务会持续写盘,退出能立刻止血。
- 需要时改用已修复的 CLI(0.142.0)干活,功能和 App 高度重叠,是绕开「App 没修」最干净的办法。
- 清理时**只删
logs_2.sqlite***,别碰state_5.sqlite等 state / memories / goals 库——那些含会话状态,删了 App 可能起不来(#24030)。 - 盯官方更新,等版本号高于
26.616、且写明减少磁盘写入的 App 版本。
常见问题
Q:升级完为什么磁盘空间没回来?
升级只停掉未来的写入,已经占用的空间要手动清(见第三步)。
Q:我已经升到 0.142.0,怎么还在写盘?
因为 0.142.0 只是部分修复。实测在 macOS + Codex 桌面 App(app-server)路径下,TRACE target=log 的高频写入仍未根治(#29532,OPEN,多人复现)。兜底两招:用「进阶」里的 trigger 在线止住 logs 表 insert;别指望 RUST_LOG=info,实测设了照样落库。剩下的盯后续版本。
Q:删掉 logs_2.sqlite 会丢对话记录或配置吗?
不会。它只是诊断日志,会话历史和配置都在别的文件里,不受影响。
Q:暂时升不了级怎么办?
如果暂时升不了级,可以把日志重定向到内存盘(tmpfs),让写入落在内存、重启即清,完全不碰 SSD。思路是把 logs_2.sqlite* 指到 tmpfs(如 Linux 的 /dev/shm、/tmp)。日志不含数据,重启丢失无所谓。Windows 没有等价的简单方案,建议直接走升级 + 清理。




