关联主题:: Codex(AI工作台)(2026.08.08)
同级:: 2026-08-09_星期日
下一级::

2026-08-09 的亲身踩坑记录。一句话结论:Codex 会话不是”一个文件夹”而是”一套相互关联的文件”,只恢复其中一个必然白费力气。 下面把存储结构、恢复步骤和预防方法完整写出来。

事故经过

我的 Codex 一直通过 CC Switch 走自定义路由。某天想用官方路由生成图片,切过去之后:

  1. 官方路由(ChatGPT 云账号)触发云同步,往本地会话目录灌入了几百条云端会话;
  2. 会话列表直接乱掉、几乎全部消失;
  3. 切回 CC Switch 也不见好转;
  4. 用时间机器只恢复了 ~/.codex/sessions/ 文件夹 → 还是不见,因为只恢复了对 4 个文件里的 1 个。

Codex 会话到底存在哪里

桌面版 Codex(ChatGPT 应用内置 Codex Framework)的会话由 5 个组件协同,任何一个与其他不一致,侧边栏列表就会出问题:

组件路径作用
正文文件~/.codex/sessions/年/月/日/rollout-*.jsonl每条会话的完整对话记录
会话目录数据库新布局常见为 ~/.codex/sqlite/state_5.sqlite;旧布局/本机也可能是 ~/.codex/state_5.sqlitethreads 表)桌面版真正的会话列表来源
会话索引~/.codex/session_index.jsonl快速检索索引
全局状态~/.codex/.codex-global-state.json项目分配、置顶、侧边栏排序
归档会话~/.codex/archived_sessions/已归档的老会话

⚠️ 最容易踩的坑:不要凭路径猜数据库。新版本常见是 ~/.codex/sqlite/state_5.sqlite,旧版本或本机实际布局可能是 ~/.codex/state_5.sqlite~/.codex/sqlite/codex-dev.db 也不是本文要恢复的 Desktop state_5.sqlite。先运行 codex-provider status 确认实际 SQLite 路径。如果用的是 ChatGPT 桌面应用里的 Codex,恢复错文件等于没恢复。

根因:为什么切换路由会让会话消失

这里其实有两层原因,第一层是数据被云同步污染,第二层(也是真正让列表”消失”的机制)是 Codex 桌面版按 model_provider 字段过滤侧边栏会话

切换路由的完整破坏链条:

  1. 官方路由走 ChatGPT 云账号 → Codex 做云同步,云端会话被灌入 state_5.sqlitethreads 表(openai provider 从 2 条涨到 401 条);
  2. 切换回 CC Switch 后,config 里的 model_provider 变成 cc-switch-official,但 threads 表里大量会话的 provider 标记与它不一致;
  3. Codex 界面按 model_provider 过滤侧边栏:只有标记与当前 provider 相同的会话才显示。于是”当前路由是 A、会话标记是 B/C/D”时,绝大部分会话被界面藏起来——数据一条没丢,但看起来像全没了;
  4. 归档会话 archived_sessions/ 也可能被清空(我的 82 个归档全没了)。

判断依据:恢复数据后,如果侧边栏仍只有零星几条,查 threads 表的 model_provider 分布——凡是与当前 config 的 model_provider 不一致的会话都会被过滤。

会话正文文件(sessions/)其实一个都没丢——丢的是”目录页”(数据库/索引/全局状态)+ provider 标记错位,Codex 按目录页去找会话,自然全不见。

摆脱 CC Switch:切换官方路由后如何恢复全部会话

如果你想彻底摆脱 CC Switch(它转发代理不支持图片生成等官方能力),改用官方套餐(ChatGPT 订阅账号),除了上面的数据恢复外,还差最后一步:把历史会话的 provider 标记统一成当前路由的 provider

我的实际情况:config 已切到官方 model_provider = "openai",但 threads 表里 672 条历史会话还标记着 cc-switch-official(245 条)和 custom(427 条),只有 2 条是 openai → 侧边栏只显示 3 条。

解法:一条 SQL 统一 provider 标记

# 先完全退出 ChatGPT 应用!
pkill -f "ChatGPT.app"
 
# 备份
cp ~/.codex/state_5.sqlite ~/.codex/state_5.sqlite.before-provider-fix
 
# 把所有历史会话的 provider 标记改成当前路由(openai)
sqlite3 ~/.codex/state_5.sqlite \
  "UPDATE threads SET model_provider='openai' WHERE model_provider IN ('cc-switch-official','custom');"
 
# 清理 WAL/SHM 残留
rm -f ~/.codex/state_5.sqlite-wal ~/.codex/state_5.sqlite-shm
 
# 验证:应全部变为 openai
sqlite3 ~/.codex/state_5.sqlite "SELECT model_provider, COUNT(*) FROM threads GROUP BY model_provider;"

配套检查(都是”切官方路由后能用”的前提)

  • config.tomlmodel_provider 必须和会话标记一致(官方订阅 = "openai"),且删除残留的 cc-switch-official / custom provider 定义(尤其是失效的 experimental_bearer_token,它会导致 401 连不上);
  • auth.json:确认是 auth_mode: chatgpt(订阅账号登录),token 未过期;
  • 网络api.openai.com 能返回响应即可(401 是正常的——只是没带凭据,Codex 会用账号 token 认证,不必惊慌;真正连不上是超时/000)。

注意:session_index.jsonl 只含 id / thread_name / updated_at没有 provider 字段,不需要同步修改。

补充:用 codex-provider-sync 同步 Provider 元数据(2026.08.10)

X 上这条经验帖(原帖)指向了
Dailin521/codex-provider-sync。它解决的正是
“切换 Provider 后,Codex 左侧项目/会话列表为空”的元数据错位问题。

它为什么有价值

它不是又一个 Provider 登录或路由切换工具,而是把切换之后容易分叉的几层状态一起对齐:

  • sessions/archived_sessions/ rollout 正文中的 Provider、model metadata;
  • SQLite threads 中的 Provider/model,以及 user-event、cwd、workspace root 等项目可见性信息;
  • 写入前的托管备份、事务回滚、恢复、旧备份清理;
  • watch 模式:监听 config.toml、SQLite 和 WAL 变化后自动同步。

所以它比本文的手工 UPDATE threads ... 更适合作为日常切换后的第一选择,也补上了“数据库已经对齐,
但项目列表/首屏排名仍不正常”的一层。它不替代 Time Machine:不会找回本地从未存在的云端会话正文,
不负责登录、认证、账号切换,也不会修复跨 Provider/account 后 encrypted_content 导致的继续对话失败。

安装与使用

# 当前稳定版本:v0.4.1
npm install --global 'git+https://github.com/Dailin521/codex-provider-sync.git#v0.4.1'
 
# 只读诊断:先确认当前 Provider、实际 SQLite 路径、rollout 分布和项目可见性
codex-provider status

已经通过 CC Switch 或官方方式完成路由/认证切换后,先完全退出 Codex、ChatGPT 和 app-server,再执行:

# 当前 config.toml 的根级 model_provider 已经是目标 Provider 时
codex-provider sync
 
# 如果确实要让工具同时修改根级 model_provider;Provider 定义必须已存在
codex-provider switch <provider-id>
 
# 从工具生成的某份托管备份恢复
codex-provider restore ~/.codex/backups_state/provider-sync/<timestamp>

sync / switch 会在目标修改前自动备份到
~/.codex/backups_state/provider-sync/<timestamp>。如果提示 SQLite 被占用,继续退出
Codex Desktop、Codex App 和 app-server 后重试;如果提示 Skipped locked rollout files,结束正在运行的
会话后再运行一次 codex-provider sync。不要把 switch 当作登录工具:它只改配置和会话 metadata,
不处理 auth.json

对我这台机器做的本次验证只运行了 status,没有执行写入:当前 Provider 为 openai,活动/归档 rollout
均已标为 openai,工具同时暴露了 691 条待修复的 SQLite user-event flags 和项目可见性诊断。这说明它
适合用来先诊断再决定是否同步,而不是一看到列表异常就直接改库。

恢复步骤(实操有效)

前提:先完全退出 ChatGPT 应用

运行中的 Codex 会持续重写这些文件(每几分钟覆盖一次),不退出就恢复 = 白恢复。退出后还要确认没有残留进程:

pkill -f "ChatGPT.app"   # 清掉残留的 kernel.js 等进程
pgrep -fl "ChatGPT.app"  # 确认没有输出

第 1 步:备份当前状态(以防万一)

mkdir -p ~/.codex-backup-20260809
cp ~/.codex/state_5.sqlite ~/.codex-backup-20260809/
cp ~/.codex/session_index.jsonl ~/.codex-backup-20260809/
cp ~/.codex/.codex-global-state.json ~/.codex-backup-20260809/
cp -a ~/.codex/archived_sessions ~/.codex-backup-20260809/

第 2 步:从 Time Machine 恢复到出问题前

找到出问题前的备份(tmutil listbackups 或查看 /Volumes/com.apple.TimeMachine.localsnapshots/Backups.backupdb/),恢复 5 个组件:

TM="/Volumes/.../Backups.backupdb/MacBook Pro/<备份时间>/Data/Users/你的用户名/.codex"
 
cp "$TM/state_5.sqlite" ~/.codex/state_5.sqlite
cp "$TM/session_index.jsonl" ~/.codex/session_index.jsonl
cp "$TM/.codex-global-state.json" ~/.codex/.codex-global-state.json
rm -rf ~/.codex/archived_sessions && cp -a "$TM/archived_sessions" ~/.codex/archived_sessions
 
# 删掉 SQLite 的 WAL/SHM 残留,避免读到旧日志
rm -f ~/.codex/state_5.sqlite-wal ~/.codex/state_5.sqlite-shm

sessions/ 文件夹一般不用动(正文文件本来就还在)。

第 3 步:验证

# 会话目录数量(我正常时 672 条)
sqlite3 ~/.codex/state_5.sqlite "SELECT COUNT(*) FROM threads;"
# provider 分布(不应有大量 openai 混入)
sqlite3 ~/.codex/state_5.sqlite "SELECT model_provider, COUNT(*) FROM threads GROUP BY model_provider;"
# 归档会话数(我正常时 82 个)
ls ~/.codex/archived_sessions/ | wc -l

一致性检查:threads 表里的 rollout_path 是否都对应真实文件(允许少量缺失)。

第 4 步:重新打开验证

打开 ChatGPT 应用,检查各项目会话是否恢复。恢复到出问题前的时间点,当天新产生的会话会丢失(本案例值得接受)。

预防清单(下次别再折腾一晚)

  1. 切换路由前先备份,就备份那 4 个文件(数据库/索引/全局状态/归档),一条命令搞定;
  2. 官方路由 = 云同步风险:只要走 ChatGPT 云账号就可能有几百条云端会话灌入本地,建议切换前先备份、切换后立刻检查 provider 分布;
  3. 不要只恢复 sessions/ 文件夹——它只是正文,目录页(数据库)才是界面读的东西;
  4. 记住 provider 过滤机制:Codex 侧边栏按 threads.model_provider 过滤会话,切换路由后历史会话”消失”多半是标记错位,一条 UPDATE 就能救回,不必反复重装/恢复;
  5. 恢复或改库时先退出应用再动手,否则运行中的进程会覆盖结果;
  6. 改完验证三连:threads 总数、provider 分布(应与 config 一致)、归档数量。
  7. 日常切换后优先执行 codex-provider status → 退出应用 → codex-provider sync,让工具连同项目可见性 metadata 一起检查;Time Machine 仍作为文件/正文真正缺失时的兜底。

相关阅读