Codex切换供应商后旧会话消失?从JSONL存储到配置排查全解析
2026/9/18 7:35:04 网站建设 项目流程

最近好几个用 Codex 写代码的朋友碰到同一个怪问题:用 cc-switch 这类供应商切换工具换了一个 API 供应商,再打开 Codex 桌面版,之前的会话全部“消失”了。有人说是不是供应商把账号数据清了,有人怀疑 Codex 历史文件损坏,还有人干脆重装了一遍工具。其实这里面九成的情况是“数据还在,只是界面不给你看了”,真正被删掉的情况反而少见。这篇文章就围绕“一切换 Codex 供应商,旧会话为什么消失”这件事,把会话文件存在哪、切换工具到底改了什么、怎么一步步排查、怎么恢复,以及以后怎么避免,一次说清楚。适合正在用 Codex CLI、Codex 桌面版或者 cc-switch 等切换工具的人参考。

1. 先搞清楚 Codex 的会话到底存哪

1.1 本地 JSONL 文件才是真正的“历史仓库”

Codex 无论是命令行版还是桌面版,历史会话都不是只存在服务端的。它会把每一段会话完整地写在本机磁盘上,默认目录是~/.codex/sessions/。这个目录下通常按项目名再分一层子目录,每个会话是一个 JSONL 文件,文件名就是一串 UUID。

JSONL 的意思是“每一行一个 JSON 对象”。会话文件的第一行通常保存会话的元信息,包括providermodelorgprojectworkspace这些身份字段。从第二行开始,才是对话过程中产生的真实事件:用户消息、助手回复、工具调用、执行结果、Token 统计等等。Codex 之所以能“恢复历史”,靠的就是把这些事件按顺序重新加载,把上下文窗口重建出来。

所以这里有个很重要的结论:切换 API 供应商这个动作,理论上不应该导致任何历史会话物理消失。除非你换的同时,有人把数据目录搬走了或者环境变量改掉了。明白这条之后,再回头看“会话消失”这个问题,思路就会清楚很多——你要找的其实是一个“为什么程序没去读旧目录”的问题,而不是“为什么数据没了”的问题。

1.2 GUI 的会话列表是“过滤后”的结果

Codex 桌面版刚打开时,并不是把sessions目录下所有 JSONL 文件一股脑全列出来。它要做一个过滤和归属判断。常见过滤维度有三个:

  • 按当前项目的project_id或工作目录过滤;
  • 按当前配置里的org_id过滤;
  • 按某个会话是否能在当前model和 API 配置下继续对话来判断是否展示。

也就是说,你看到的会话列表,本质上是“当前环境 + 当前身份 + 当前项目上下文”筛选之后的结果。只要切换供应商导致这些关键字段变化,GUI 就会认为那些旧会话不属于当前上下文,然后不显示出来。

这就是为什么“供应商切换”和“旧会话消失”会被绑定在一起。并不是切换工具删了文件,而是它把 Codex 的“视角”改了。会话文件还在原来的地方躺着,只是 GUI 不再主动把它们读出来给你看。

2. 切换供应商时,哪些动作会把旧会话藏起来

2.1 配置重写导致的路径漂移

cc-switch 这类工具切换供应商时,核心动作就是重写 Codex 的配置文件config.toml。它会把你选中的供应商对应的modelapi_base_urlorg_idproject_id写进配置,替换掉之前的内容。

问题往往出在细节上。我手头就有一个例子:A 供应商的配置里project_idproj_a_123,B 供应商的配置里project_idproj_b_456。切到 B 之后,Codex 桌面版加载的是proj_b_456对应的项目会话,而旧会话文件仍然躺在~/.codex/sessions/proj_a_123/下面。你说它删了吗?没有。你说它“消失”了吗?界面上确实是看不见了。

还有一种更隐蔽的路径漂移,是某个供应商配置里额外设置了CODEX_HOME环境变量或自定义数据目录。切换过去之后,Codex 跑到新目录里去读历史,旧目录里的 sessions 自然全部“消失”。我甚至见过有人把CODEX_HOME指到了一个完全没数据的路径,打开界面空空如也,排查半天才发现是环境变量的问题,跟供应商本身没关系。

2.2 cc-switch 本地转发层切换失败

cc-switch 这类工具的实现方式,一般会在本机起一个本地转发层。Codex 的api_base_url指向http://127.0.0.1:14540/v1之类的地址,由这个转发进程把请求再送到当前选中的供应商 API。这样切换供应商时,Codex 配置可以保持基本不变,只要转发层换个目标就行。

听起来很优雅,但这个设计有一个明显弱点:转发层必须一直在且状态正确。一旦切换过程中转发层没起来、端口被占用、或者目标服务地址没更新,就会出问题。

很多朋友遇到的cc switch local proxy failed while handling codex endpoint /responses就是这个场景的典型表现。具体来说,会话列表可能还正常,但当你点开一个旧会话想恢复历史时,Codex 要先向后端发一个/responses请求来确认能否继续对话。这个请求先打到本地转发层,转发层又没就绪,结果就是界面一直转圈,最终把整条会话标记为无法加载。

这看起来就像“旧会话消失了”,但文件其实好好的。你只需要重启转发层或切换工具,让本地代理恢复正常,会话列表很可能就回来了。

2.3 模型或账号权限引发的恢复中断

第三个坑是模型兼容性,这个非常容易被误判成“会话被删”。

比如你之前用官方 ChatGPT 账号下的gpt-5.6-sol这个预览型号写了一段很长的代码,后来切到一个第三方聚合供应商,对方只提供gpt-5-codex或者gpt-4.1这类模型。你打开旧会话,Codex 读取到文件头部的model字段是gpt-5.6-sol,于是它拿这个模型名去请求新的 API 网关。网关直接拒绝,报类似the 'gpt-5.6-sol' model is not supported when using codex with a chatgpt account的错误。

此时 Codex 无法正常恢复这个会话,GUI 就会把这条历史从“可继续会话”里剔除,或者点了之后直接报错。从用户视角看,就是“切换供应商之后旧会话没了”。从技术视角看,会话文件还在,只是元信息里的模型和当前供应商的模型清单不匹配,导致加载链路中断。

所以排查这个问题时,一定不要只看界面,要落到文件层面去验证。

3. 三步排查法:确认旧会话是真没了还是看不见

3.1 第一步:看文件系统,判断数据是否物理存在

我遇到这种问题,第一件事永远不是去翻 GUI 设置,而是打开终端检查会话文件。

先跑这几条命令:

ls -la ~/.codex/sessions/ find ~/.codex -name "*.jsonl" -mtime -30 | head -20 echo $CODEX_HOME

第一条看默认目录下有哪些会话子目录;第二条找最近 30 天内的会话文件;第三条确认当前环境变量有没有被改过。如果文件都在,说明物理数据没有丢,问题大概率出在加载和过滤逻辑上。

如果默认目录是空的,而且你确实设置过CODEX_HOME或自定义数据路径,那就用下面的命令全局找一下:

find / -name "*.jsonl" -path "*codex*" 2>/dev/null

我有一次帮人排查,会话全在/data/codex_home/sessions下面,因为他的CODEX_HOME早就被某个配置脚本改到了这个路径,而 GUI 默认读的却是~/.codex

另外,如果想快速看一批会话分别属于哪个项目和模型,可以用这个命令把每个 JSONL 的第一行解析出来:

for f in ~/.codex/sessions/*/*.jsonl; do head -1 "$f" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('project',''), d.get('model',''), d.get('org',''))" 2>/dev/null | sed "s|^|$f |" done

不要嫌麻烦,这一步能直接告诉你会话是按什么维度拆分的,后面排查思路会清晰很多。

3.2 第二步:检查当前配置指向的存储和身份参数

确认文件还在之后,下一步看~/.codex/config.toml

重点看这几个字段:

  • model:当前用的模型
  • api_base_url:请求发送到哪
  • org_id:组织标识
  • project_id:项目标识
  • 如果有session_pathhistorydata_dir之类的字段,也要留意

如果api_base_url指向的是127.0.0.1这样的本地地址,说明 Codex 正在走本地转发层。这时候再去切换工具里看“当前选择的供应商”和“实际生效的供应商”是否一致。很多切换工具在界面上显示已经切了,但配置文件因为权限或者格式问题没写进去,实际启动的 Codex 还在用旧配置。

还有一点容易被忽略:有些工具不是改config.toml,而是通过启动 Codex 时注入环境变量来改变配置。如果你从 GUI 里手动启动 Codex,和通过切换工具启动 Codex,两者读到的可能是完全不同的配置。这样就会出现“工具里显示切到了新供应商,但 Codex 界面里还是旧供应商”的错位。

3.3 第三步:检查切换工具的本地转发层和日志

如果前两步都没问题,接下来的重点就是日志。

cc-switch 这类工具一般都有日志窗口,或者会把日志写入某个本地文件。出现local proxy failed时,去日志里看它具体失败在哪一步:是端口被占用、是目标地址连不上、还是证书或模型名校验没过。

常用端口检查命令:

lsof -i :14540 netstat -ano | grep 14540

如果看到端口被别的进程占着,把那个进程找出来杀掉,或者修改切换工具里的本地端口,然后重启工具。

如果看到的是类似cc gui 尚未配置 ai 供应商或未授权使用本地配置的提示,多半是切换工具升级之后,旧配置格式不兼容,或者配置文件权限不对,工具认为自己没有权限覆盖。这种情况直接去供应商管理里重新选一次供应商并保存,让它重新生成配置,一般就能恢复。

4. 恢复旧会话的四种可行办法

4.1 切回原供应商,最快最稳的验证手段

如果只是切换后看不到旧会话,而且你还没动过任何数据目录,最快的验证方式就是先切回原来的供应商。

切回去之后,Codex 加载的路径、模型、org、project 又都和旧会话的元信息对上了,会话列表大概率会恢复。原理很简单:旧会话没有丢,只是之前的切换让当前上下文和旧会话不匹配,匹配关系恢复自然就看到了。

有个操作细节:切回去之前先把当前配置备份一下。避免来回切换时把两边配置都覆盖乱。我一般会在切换前先这样做:

cp ~/.codex/config.toml ~/.codex/config.toml.bak

这个动作成本极低,但能省很多事。

4.2 手工迁移 sessions 目录

如果你的问题是数据目录被改了,或者你确定旧会话文件在一个新环境不读取的路径下,那就需要手工迁移。

第一步先备份:

tar -czf codex_sessions_backup.tar.gz -C ~ .codex/sessions

第二步找到旧会话文件:

find ~/.codex -name "*.jsonl"

第三步确认目标目录的结构。一般sessions下第一层是项目名,第二层是 JSONL 文件。把旧文件复制过去时也要保持这种结构,否则 Codex 不知道按什么项目来归属这些会话。

最省事的做法是直接复制整个目录:

cp -r ~/.codex/sessions ~/new_codex_home/sessions

然后把CODEX_HOME环境变量指向新目录,再启动 Codex。

这里要提醒:复制解决的是“路径不对”的问题,不解决“模型不匹配”的问题。如果新供应商不支持旧会话里的模型,迁移之后照样打不开。所以迁移完还要检查模型字段。

4.3 修改 JSONL 元信息匹配新供应商

当你确定旧会话文件都还在,但新供应商不接受旧模型时,可以小范围修改 JSONL 第一行的元信息,把model字段替换成新供应商支持的模型名。

先备份,再用脚本批量处理:

import json, glob, shutil model_map = { "gpt-5.6-sol": "gpt-5-codex", "gpt-6-astra": "gpt-5-codex", } for path in glob.glob("$HOME/.codex/sessions/**/*.jsonl", recursive=True): with open(path, "r", encoding="utf-8") as f: lines = f.readlines() if not lines: continue changed = False try: header = json.loads(lines[0]) except Exception: continue old_model = header.get("model") if old_model in model_map: header["model"] = model_map[old_model] lines[0] = json.dumps(header, ensure_ascii=False) + "\n" changed = True if changed: shutil.copy2(path, path + ".bak") with open(path, "w", encoding="utf-8") as f: f.writelines(lines) print("updated", path)

注意,这里只改model键,其他的不动。改完再用 Codex 打开会话,至少列表能出现。需要理解的是,这种处理只能让会话“可打开”,上下文里如果大量引用了旧模型的行为,实际对话质量可能有差异。但对于“找回列表”这个目标来说已经足够了。

如果你的旧会话问题不是模型,而是org_idproject_id不匹配,思路类似,只替换对应的键就行。不过强烈建议在改之前多做一步验证:用head -1读几个文件看看元信息差异,确认你要替换的字段到底是什么格式。

4.4 日常备份与导出习惯

恢复办法说到底都是补救,最好的策略是不让问题发生。

我现在已经养成习惯,每次切换供应商之前,先跑一次备份:

tar -czf ~/codex_sessions_$(date +%Y%m%d_%H%M%S).tar.gz -C ~ .codex/sessions

这个压缩包不会很大,但一旦切换后出问题,我能随时回到切换前的完整状态。如果你的 GUI 客户端有导出会话功能,也可以用,但从可靠性和完整性来看,直接压缩目录是最好用的。

另外一个有意思的点:JSONL 文件本身就是文本格式,意味着你可以用grep直接搜索历史内容,比如查某段代码片段在哪些会话里出现过。这个技巧不针对“会话消失”,但每次备份时用grep -l "某个报错信息" ~/.codex/sessions/**/*.jsonl找历史踩坑记录特别方便。

5. 避免下次再踩同一个坑的配置规范

5.1 统一或固定 org_id / project_id

如果你需要在多个供应商之间来回切换,最好的办法是尽量让不同供应商配置里的org_idproject_id保持一致,只在api_base_urlmodel上做区分。

为什么这样建议?因为 Codex 的会话列表默认就是按项目维度过滤的。如果每个供应商的project_id都不同,那么每次切换都会导致“当前项目”变化,旧会话自然全部被隐藏。而如果你把project_id固定成同一个值,切换供应商后会话仍然归属同一个“项目”,GUI 过滤时不至于把历史全部藏起来。

实际使用中我也明白,有些供应商会强制绑定不同的org_id,那这种情况下不要硬改,而是采用另一个思路:用不同的工作目录来区分场景,而不是依赖项目标识。工作目录的变化更直观,也好排查,也方便迁移。

5.2 把 Codex 配置文件纳入 Git

config.toml是一切配置的源头。我建议在~/.codex下初始化一个 Git 仓库,把关键配置纳入版本管理。

cd ~/.codex && git init git add config.toml 2>/dev/null || true git commit -m "before switch supplier"

每次切换供应商前后都 commit 一次。切换后如果发现会话消失,直接git diff就能看到config.toml到底被改了哪些字段。这一步往往能让你在五分钟内定位问题,而不用靠猜。

如果只是临时排查不想建仓库,也可以直接用cp备份配置文件,效果差不多,只是没有历史版本对比那么方便。

5.3 升级切换工具前先读 release notes

cc-switch 这类工具更新很频繁。新的供应商格式、新的配置模板、新的本地转发层实现,都可能导致旧会话加载逻辑变化。

我遇到过一种情况:工具升级后,它把 Codex 的api_base_url从“直接指向供应商”改成了“统一走本地代理”。这个改动本身没问题,但旧版本生成的会话文件里记录的是直连时的上下文信息,新版本 GUI 在恢复时校验身份不通过,于是历史列表变空。

所以升级前一定要看 release notes。升级之后,去供应商管理里重新保存一次当前供应商配置,让工具重新生成配置文件和本地转发地址,这能避免大部分兼容性问题。

如果升级后出现本地转发层频繁报错,优先怀疑端口占用。常见处理方式:

lsof -i :14540 kill -9 <pid>

然后重启工具,让转发层重新绑定端口。

6. 会话消失常见问题速查表

平时排查多了,我把常见场景整理成一个速查表,遇到问题先对着找,比闷头翻日志快得多。

现象可能原因处理办法
切换供应商后会话列表为空配置重写导致项目维度或数据目录变化检查CODEX_HOMEproject_id,切回原供应商验证
点开旧会话一直转圈或提示重新连接本地转发层未就绪或崩溃查看 cc-switch 日志,重启转发层或工具,检查端口占用
local proxy failed while handling codex endpoint /responses本地代理转发目标异常查看日志中实际错误,重启工具,换本地端口,升级工具
会话列表还在但点开就报模型不支持旧会话中的model字段新供应商不支持修改 JSONL 头部的model字段,或改 Codex 配置里的模型
提示“尚未配置 AI 供应商或未授权使用本地配置”切换工具未正确写配置,或格式不兼容重新进入供应商管理,保存一次目标供应商配置
切换后界面加载的是另一个项目的历史GUI 按项目过滤导致旧会话不显示切换回原项目/工作目录,或清理筛选条件

这些问题的本质其实就一句话:Codex 的会话历史是本地的,并且强绑定当前项目上下文;供应商切换只应该改变“请求发到哪里”,不应该改变“数据放在哪里”。凡是违反这个原则的工具行为或配置写法,都会让你觉得会话“消失”了。

我自己在实际操作中的体会是,遇到这种问题,第一反应永远不要想着恢复数据,而是先确认数据是不是真的没了。只要 JSONL 文件还在,会话就还有救。我现在已经形成了固定流程:切换前备份config.toml和 sessions 目录,切换后发现异常先看环境变量和配置 diff,最后才去看 GUI 和日志。

最后再分享一个小技巧:如果你希望切换供应商后旧会话依然能显示,尽量把不同供应商配置成相同的org_idproject_id,只在api_base_urlmodel上做区分。这样 GUI 的会话过滤不会因为项目上下文变化把历史藏起来,切换供应商这件事对你来说就真的只是“换一个请求出口”,而不是“换一个世界”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询