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