Codex 无法归档对话、归档失败怎么办?先分清是命令没找到会话,还是文件没挪成
「归档按下去没反应」和「归档报了个错」是两回事,但它们经常被同一句话描述成「Codex 无法归档」。这篇不讲归档的正常用法(那部分在 Codex 会话管理实战:归档、取消归档、删除与按名字找回 里),只讲归档失败时怎么定位。
先交代来源边界:下面所有关于「归档做了什么、失败会说什么」的说法,都来自 openai/codex 这个开源仓库的源码,抓取快照是 main 分支 commit 339751715c64496cb86246bfb3935f40e309dd3d(2026-08-24)。我没有在本机执行过归档动作,所以你不会看到「我跑了一遍发现……」这种话——报错文案是从源码里的字符串字面量抄出来的,行为是从代码路径读出来的。版本会变,文案也可能跟着变,以你自己那一版的实际输出为准。
一、三十秒分诊:先看报错的第一个词
Codex 归档这条链路上,报错来自两个完全不同的层,分辨方法特别直白:
如果报错以 No ... session found matching 开头,说明命令连目标都没解析出来,文件系统那边一根手指都没动。这是「找不到会话」类。
如果报错里出现 failed to archive thread / already has an active writer / no rollout found for thread id,说明目标已经找到了,卡在了挪文件或改状态那一步。这是「挪不动」类。
如果你根本没看到报错,只是界面上那个快捷键不出现、或者 /archive 被拒绝,那既不是前者也不是后者,是入口层的可用性判断把你拦在门外了。
这三类的排查方向完全不同,混着试只会浪费时间。下面分开说。
二、归档命令实际做了哪几件事
codex-rs/tui/src/session_archive_commands.rs 开头那段模块注释把架构说得很清楚:codex archive、codex delete、codex unarchive 是同一份实现,它们都是 app-server 的瘦客户端——先把你给的 UUID 或会话名解析成一个会话,再调对应的 RPC。也就是说,命令行这一层只负责「解析目标」,真正干活的是后面的线程存储层。
解析目标这一步有个容易踩的规则。CLI 的参数定义里,这个位置参数的说明原文是:会话 id(UUID)或会话名,如果能解析成 UUID,UUID 优先。代码里也确实是先尝试把你输入的字符串当 UUID 解析,解析成功就直接拿去调 RPC,根本不会再去按名字查一遍。
按名字查的时候,作用域是分动作的,这一点是归档失败的高发区:
| 动作 | 搜索范围 | 找不到时的报错 |
|---|---|---|
| archive | 只搜活跃会话 | No active session found matching ‘目标’. |
| unarchive | 只搜已归档会话 | No archived session found matching ‘目标’. |
| delete | 活跃和已归档都搜 | No active or archived session found matching ‘目标’. |
看出问题了吗?归档只在活跃会话里按名字找。 所以一条已经归档过的会话,你再拿名字去 codex archive,收到的会是「No active session found matching」——这句话读起来像「没这个会话」,实际含义是「活跃列表里没有」。这是我认为最容易被误读成「无法归档」的一种情况。
存储层那边(codex-rs/thread-store/src/local/archive_thread.rs)的动作顺序是:先拿一组锁 → 扫一遍 rollout 引用索引 → 解析出这条会话当前的 rollout 文件 → 建好归档目录 → 逐个 rename 挪过去 → 最后在状态库里打上归档标记。
两个细节值得记住。第一,挪文件和改状态库是两步,任何一步失败都会把已经挪过去的文件搬回来,源码里对应的是一个叫 restore_rollout_moves 的回滚调用,挪文件失败、状态库写失败都会走它。所以失败之后你的会话文件通常还在原位,不至于半路丢掉——但如果连回滚本身都失败了,报错会变成「失败 A;并且恢复移动失败 B」这种复合句,那种情况就得手动去目录里看了。
第二,归档目录是扁平的。归档时目标路径就是 archived_sessions 目录直接拼上原文件名,不带年月日子目录;而活跃会话是按年/月/日分层存的。这个不对称在下面讲取消归档时会咬人。
三、「挪不动」类的四种形态
形态一:这条会话正在被写
报错原文是 thread <id> already has an active writer,错误类型是冲突。触发它有两条路径:一条是当前进程内的活跃记录器里还挂着这条会话,另一条是跨进程的文件锁抢不到——存储层会在锁目录里对每条会话开一个 <会话 id>.lock 文件并尝试加锁,加不上(WouldBlock)就直接返回同一句冲突文案。
所以这句话真正的意思是:有人正开着这条会话。 可能是另一个终端窗口里还跑着 Codex,可能是你自己在当前会话里试图归档当前会话。它不是数据损坏,是并发保护。
有意思的是,源码里还有一个针对陈旧锁的清理动作,在第一次加锁时会尝试清一遍失效的锁文件,清理失败只写一条 warn 日志、不影响主流程。也就是说进程崩溃留下的死锁文件不至于永久卡住你,但这个清理是「尽力而为」的。
形态二:找得到会话,找不到文件
报错原文是 no rollout found for thread id <id>。这条出现在解析当前 rollout 路径返回空的时候。归档要挪的是磁盘上的 rollout 文件,索引里有这条会话、磁盘上却没有对应文件,就会走到这里。
一个很现实的诱因是有人手动进 sessions/ 目录删过文件。关于会话文件到底落在哪个目录、目录结构长什么样,Codex 的 .codex 目录到底在哪 那篇讲得更细。手删文件带来的索引与磁盘不一致,通常不会在删的当下报错,而是在你下次归档时才炸出来。
形态三:路径或文件名不合规
有两句报错属于这一类:rollout path ... must be in sessions directory 和 rollout path ... has an invalid filename。
第一句来自一个叫 scoped_rollout_path 的校验:它会把路径规范化,然后要求结果必须落在预期的根目录之内(归档时是 sessions 目录,取消归档时是 archived 目录)。规范化本身失败也会归到这句里。第二句来自文件名校验,如果从路径里解析不出 rollout id,就判定文件名非法。
这两句基本都指向「有人在目录里动过手」——重命名过文件、做过软链接、或者把会话文件挪到了别处。
形态四:文件挪成了,状态库没写进去
报错原文是 failed to update archived thread metadata: ...。前面说过,这一步失败会把已经挪走的文件全部搬回来。所以这种失败的观感是「命令报错了,但什么也没变」,容易让人以为是命令没执行。
四、入口层:你可能根本没触发到归档
这一类最不像故障,但按曝光词的分布看,我猜相当一部分「无法归档」其实是这里。
在会话里用 /archive。 斜杠命令表里它的说明是「归档这个会话并退出」,而且它被明确列在「任务进行中也可用」的那一组里。触发后会先弹一个确认框,标题是 Archive this session?,副标题写明这会归档当前会话并退出 Codex。选「不」就回到会话,选「是」才真正发出归档事件。所以如果你按了 /archive 却发现什么也没归档,先想想那个确认框你选的是哪一项。
/archive 有两种被直接拒绝的情况,报错文案分别是:
A thread must start before it can be archived.—— 会话还没真正开始,没有可归档的对象。'/archive' is unavailable in side conversations. Press Ctrl+C to return to the main thread first.—— 你在子会话里,得先回主线程。
如果确认之后归档失败,界面上会打出 Failed to archive current thread: <错误>,并且不退出(正常成功是会退出的)。所以「点了是,Codex 没退出,还多了一行红字」= 归档失败,那行红字后半段就是真正的原因,按上一节对号入座。
在选择器里按 Ctrl+A。 这个快捷键的可用性有三个条件同时成立才行:当前选择器是恢复会话用的(不是分叉那种)、当前看的不是已归档列表、并且 Ctrl+A 没有被你的键位配置占用——源码里会检查列表键位表和组合键前缀,只要有一个占了 Ctrl+A,这个归档快捷键就不提供。自定义过键位的人尤其要注意这条。
选择器里还有两句拦截文案值得记:选中行没有会话 id 时是 Selected session does not have a thread ID.;试图在选择器里归档你正开着的那条会话时是 Use /archive to archive the current session and exit.——这句其实是在给你指路,不是报错。归档真失败了才会显示 Failed to archive session: <错误>。
另外,归档状态机在非空闲时会直接忽略后续请求。连按几下没反应,属于设计如此,不用怀疑键盘。
五、验收:怎么确认这次到底成没成
成功输出是有固定形状的。CLI 成功时打印的是 Archived session <名字> (<id>).,没有名字就退化成 Archived session <id>.。取消归档和删除分别是 Unarchived 和 Deleted 开头,同一套格式。看到这句就是成了,没看到就是没成,不用去猜。
文件层面的验收更硬:归档成功后,那个 rollout 文件应该从活跃会话目录消失,出现在归档目录里,文件名不变。仓库里的单元测试就是这么断言的——归档后原路径不存在、归档目录下同名文件存在。你可以照这个思路自己核一遍。
至于磁盘占用有没有真的降下来,那是另一个话题,Codex 会话文件把磁盘吃光了怎么办 里讲了归档和真正腾出空间之间的差距——归档只是挪了个地方,磁盘该占还占。
六、取消归档为什么更容易失败
如果你归档成功了、想撤回却撤不回来,原因很可能在文件名上。
前面说归档目录是扁平的,那取消归档时怎么知道该把文件放回哪个年月日子目录?答案是从文件名里解析日期。源码里会对归档文件名做一次日期解析,解析不出来就报 rollout path ... missing filename timestamp,整个取消归档失败。
这意味着:归档之后千万别去重命名归档目录里的文件。 归档时校验的是「文件名能否解析出 rollout id」,取消归档时额外还要求「文件名能解析出年月日」。你改一个字,归档进去的东西就可能再也回不来了。
取消归档还有一步是把恢复出来的文件修改时间刷成当前时间,这一步失败同样会触发回滚,报错是 failed to update unarchived thread timestamp。另外,如果你拿一条压根没归档的会话去 codex unarchive,报错是 no archived rollout found for thread id <id>——注意这句和归档那句「no rollout found」只差两个词,别看串了。
七、几个「指望不上」的地方
一、归档不接受批量,也不认通配符。 CLI 那个位置参数是单个字符串,一次一条。想批量清理,只能自己在外面套循环。
二、子会话归档失败不会告诉你。 归档一条会话时,它派生出来的子会话也会一并归档;但源码里对子会话的失败只写一条 warn 日志,不会让整个命令失败——只要第一条成功了,命令就返回成功。所以「主会话归档了、某个子会话还留在活跃列表里」这种局部失败,光看命令输出是看不出来的。
三、名字匹配是精确匹配,不是模糊搜索。 按名字查会话走的是精确匹配路径,而且分层级:一种历史模式下允许会话名为空(名字可能只存在于旧索引里),另一种模式下要求名字严格相等。差一个空格、差个大小写,就是「找不到」。拿不准就用 UUID,UUID 优先级更高、也不受这些规则影响。
四、删除比归档更不宽容。 删除在非交互终端下,如果不带强制标志会直接拒绝,报错原文是 cannot confirm session deletion without an interactive terminal; rerun with --force and a session UUID;而那个强制标志的说明明确写了「目标必须是 UUID」。交互式确认里还有一句提醒:这个操作不可撤销,子代理会话也会一并删除。所以在脚本里做清理,先想清楚你要的是归档还是删除。
五、桌面应用那侧的归档不归这些命令管。 本文所有依据都来自 CLI 与终端界面的源码路径,别拿去套桌面端。想搞清楚两侧的分工,Codex 桌面版和命令行到底该用哪个 那篇有对照。
收个尾
「无法归档」这四个字,落到 Codex 上其实是四条不同的路:命令没找到会话(看 No ... session found matching)、会话被别人占着(看 already has an active writer)、文件层面出了状况(看 failed to archive thread 和路径类报错)、以及你压根没触发到归档(确认框选错、快捷键被占、在子会话里)。
排查顺序建议就按这个来:先确认有没有看到 Archived session ... 这句成功输出 → 没有的话看报错第一个词属于哪一类 → 再去对应的层动手。 别一上来就去目录里手动挪文件,那是把「归档失败」升级成「索引不一致」的最快方法。
相关阅读
- Codex 会话管理实战:归档、取消归档、删除与按名字找回
- Codex 会话续跑与分叉实战:resume、—last 与 fork 怎么用才不丢上下文
- Codex 会话文件把磁盘吃光了怎么办
- Codex 的 .codex 目录到底在哪
- Codex 桌面版和命令行到底该用哪个
本文依据 openai/codex 开源仓库 main 分支源码整理,快照为 commit 339751715c64496cb86246bfb3935f40e309dd3d(2026-08-24),涉及文件包括 codex-rs/tui/src/session_archive_commands.rs、codex-rs/thread-store/src/local/archive_thread.rs、codex-rs/thread-store/src/local/unarchive_thread.rs、codex-rs/thread-store/src/local/helpers.rs、codex-rs/thread-store/src/local/writer_lock.rs、codex-rs/tui/src/resume_picker/archive.rs、codex-rs/tui/src/slash_command.rs、codex-rs/tui/src/chatwidget/slash_dispatch.rs、codex-rs/tui/src/app/event_dispatch.rs、codex-rs/cli/src/main.rs。报错文案为源码中的字符串字面量,未在本机执行归档动作验证;代码会随版本变化,以你当次运行的实际输出为准。
留言讨论
评论发布后会被人工复核,违规内容将被删除。
如果发表没有反应,可以前往联系我们告诉我们。