最近在使用 Codex Desktop 时遇到一个问题:切换 API/provider 之后,重启 App,左侧历史会话几乎都不见了,只剩下最近几条记录。第一眼看上去像是会话被删了,但实际排查下来,这不是数据丢失,而是旧会话被 App 的可见性逻辑过滤掉了。
问题出现后,我先检查了本地 Codex 数据目录。Codex 的会话数据主要分布在几个地方:
~/.codex/state_5.sqlite
~/.codex/session_index.jsonl
~/.codex/sessions/**/*.jsonl
~/.codex/.codex-global-state.json最开始只修复 session_index.jsonl 并没有解决问题。进一步检查发现,SQLite 数据库和索引里其实都有完整记录:数据库里有 255 条 thread,索引里也有 255 条,但 Codex Desktop 的线程列表接口只显示了几条。这说明问题不在“数据不存在”,而在“App 没把它们展示出来”。
真正的关键线索来自 rollout 日志,也就是 ~/.codex/sessions/.../rollout-xxx.jsonl。这些旧会话的第一行通常是 session_meta,里面保存了会话元数据,例如:
{
"type": "session_meta",
"payload": {
"thread_source": "user",
"model_provider": "custom"
}
} 部分旧会话的 model_provider 是之前自定义 API/provider 写入的值,例如 customflashrain。实验发现,只把某个旧会话 rollout 第一行里的 model_provider 改成 Codex Desktop 当前能识别的 provider,比如 ai,这个会话就能重新出现在列表里。
也就是说,根因是:
Codex Desktop 在重建或过滤会话列表时,不只看 SQLite 和 session_index.jsonl,还会参考 rollout 日志里的 session_meta.payload.model_provider。当这个 provider 是当前版本不认识的旧值时,会话并没有被删除,但会被 UI 过滤掉,看起来就像“历史会话丢失”。
最终修复策略是:
1. 备份 state_5.sqlite、session_index.jsonl、global state 和被修改的 rollout 文件
2. 只处理顶层用户会话,不处理 subagent 会话
3. 将旧 provider 值归一化为 ai
4. 同步修复 SQLite threads.model_provider
5. 同步修复 rollout 第一行 session_meta.payload.model_provider
6. 重建 session_index.jsonl
7. 展开侧边栏 chats/threads 区域
8. 对 SQLite 做 WAL checkpoint,确保修改落盘
9. 完全退出 Codex Desktop 后重新打开修复后重新检查:
DB 会话数:255
索引会话数:255
缺失会话:0
旧 provider 残留:0Codex Desktop 侧也重新能看到旧会话了。部分很旧的会话在搜索里可能仍不命中,但通过 thread id 可以读到,说明数据本身已经恢复,剩下更多是 App 搜索缓存或加载策略的问题。
这次排查最大的经验是:不要把“UI 看不到”直接等同于“数据丢失”。尤其是桌面端应用,经常会有多份状态源:数据库、索引文件、日志文件、UI 持久化状态。只修其中一个地方,很可能会被另一个状态源重新覆盖。
后续如果再次切换 API/provider,只要本地 ~/.codex/sessions 和 state_5.sqlite 还在,会话大概率不会真的丢。即使左侧又看不到,也可以通过同样的思路恢复:先备份,再检查 provider 元数据,再重建索引。