记一次Codex历史会话空白排障

文章发布时间:

最后更新时间:

文章总字数:
927

页面浏览: 加载中...

现象

Codex 能正常启动,但历史会话列表是空白,配置页还报过一次类型错误。

后来又复现了一次。换 API、再用 cc switch 切 provider 之后,Codex 直接报了 401,历史会话也跟着像是没了。

排查

最开始我怀疑是 SQLite 丢数据,结果不是。.codex/state_5.sqlite 里 thread 还在,归档也都在。

继续往下翻后,发现真正控制历史列表的是 app-serverthread/list。这里踩了两个点:

  • config.toml 里有几项配置现在这版 Codex 已经不认了,所以得先把它改成能正常读取的最小配置
  • thread/list 不只看 .codex/state_5.sqlite,还会读每个 rollout 文件首行的 session_meta.payload.model_provider

关键字段

.codex/config.toml 里,关键字段大概是这样:

1
2
3
4
model = "gpt-5.4"
wire_api = "chat_completions"
model_provider = "openai"
base_url = "第三方 API 站"

历史会话文件,也就是 .codex/sessions/.../rollout-*.jsonl 的首行,关键字段大概是这样:

1
2
3
4
5
6
7
8
9
{
"type": "session_meta",
"payload": {
"cwd": "/某个目录",
"source": "vscode",
"cli_version": "0.116.0-alpha.10",
"model_provider": "crs"
}
}

为什么历史会“消失”

这次问题的核心,其实就是 model_provider 不一致。

我切过 provider 之后,当前配置已经在用新的 provider,但老历史文件首行里还保留着之前的 model_provider

我当时统计到的情况大概是:

  • 一部分历史写的是 crs
  • 一部分历史写的是 openai

Codex 列历史时会参考这个字段。结果就是:model_provider 对不上的那部分历史,直接被 thread/list 过滤掉了。

所以看起来像历史没了,其实不是文件丢了,而是没被列表接口显示出来。

根因

所以这次的核心不是历史真丢了,而是历史记录里的 model_provider 对不上了。

这也是为什么单改 .codex/state_5.sqlite 不够,必须连 rollout 首行的 model_provider 一起处理。

类似情况

我查到了一些接近的公开线索,比如历史恢复、线程列表重建、模型列表过滤,还有 xhigh 配置报错。

但我没有找到和这次完全一样的现成答案。至少在我查到的公开资料里,没有人把问题直接落到 rollout 首行的 model_provider 分裂上。

修复

我最后实际动的是这些:

  • 清掉无效的 .codex/config.toml
  • .codex/state_5.sqlite 里的 backfill_state 调回可读状态
  • 直接起一个临时 codex app-server,手工发 initializethread/list 请求,看它到底返回空数组还是能返回历史
  • .codex/sessions/.../rollout-*.jsonl.codex/archived_sessions/.../rollout-*.jsonl 首行里的 model_provider 统一回当前活动 provider
  • .codex/state_5.sqlitethreads 表导出 JSON 行,重新写回 .codex/session_index.jsonl

这次恢复历史,真正关键的就是两件事:

  • 改 rollout 首行里的 model_provider
  • threads 表把 .codex/session_index.jsonl 重新生成出来

另外,这个问题基本可以确定和 cc switch 有关系。它切换 Codex provider 时会改 live 配置,但不会顺手处理历史 rollout 首行里的 model_provider,也不会重建 .codex/session_index.jsonl。当前 live 配置切过去了,历史元数据却还停在旧值,最后就把历史列表搞空了。

结论

这类问题很容易让人判断错方向。看起来像历史没了,实际上数据还在,只是 model_provider 对不上,最后被历史列表过滤掉了。

参考链接