Codex 报错 thread not found 排查实录:会话线程丢失
现象:提交消息时报错
thread not found: uuid,新建对话偶尔正常,旧对话直接报错,退出登录重新登录后问题消失。
问题现象
业务日志报错片段:
20261014 13:01:01 || 提交消息时出错 thread not found: 01a0a40a-1812-7832-bed5-2d98a207c41b
表现特征:
- 打开历史会话,发送消息直接抛出
thread not found; - 部分场景新建对话可以正常发消息,老会话完全不可用;
- 接口返回找不到指定 thread UUID,服务端提示会话线程不存在;
- 临时解决方案:客户端退出登录,重新登录,原有会话恢复正常,报错消失。
注意:不是 thread 被删除,thread 实体实际还在后端数据库,客户端侧状态异常导致匹配失败。
根本原因分析
thread not found 字面含义:服务端找不到传入的 thread_id 。
但这里特殊点:重登录就恢复,说明数据库里 thread 记录没有被删除,属于客户端‑服务端会话状态不同步,分几种诱因:
1. 本地缓存的 thread 上下文与后端鉴权会话错位(最高概率)
- 浏览器/客户端本地存储(localStorage / indexDB)缓存了旧的
threadId、会话上下文、旧的 token; - 用户 token 发生静默刷新、后端会话版本更新,但前端缓存没有同步更新;
- 前端拿着旧 token + 旧 threadId 请求接口;后端鉴权通过,但当前登录会话上下文映射关系失效 ,后端判定该 thread 不属于当前会话,直接返回
thread not found。
退出登录动作:清空本地全部缓存、销毁本地旧 token、旧会话映射;重新登录后拉取最新会话上下文,thread 与账号重新建立绑定关系,故障自愈。
2. Token 静默更新但前端未感知
很多 AI 客户端会后台自动刷新 access_token:
- token 更新成功,但是前端没有刷新内存里的会话上下文实例;
- UI 展示历史对话列表正常,点击发送时携带的是新旧混合状态;
- 后端拿到 threadId,在当前有效会话域查不到,抛出 thread not found。
表现:页面看着一切正常,只有发送消息那一刻报错,列表能看到历史会话。
3. 多标签页 / 多客户端竞态
同一账号多处登录:
- 一个标签页 token 更新,另外标签页继续使用老状态;
- 操作历史对话,就会出现 thread not found;
- 刷新页面有时不一定修复,因为 indexDB 缓存还在;必须登出清空存储。
4. 后端会话中间件缓存过期
后端会维护「账号 ↔ thread 集合」内存缓存,不是每次都查DB。
当该层缓存过期、未及时重建,前端依旧使用老 threadId 请求,返回找不到;重新登录触发后端重建账号‑thread 映射缓存。
数据库 thread 数据完好无损,只是内存映射缓存失效。
排查区分要点
| 现象 | 根因判断 | 处理手段 |
|---|---|---|
| 登出重登立刻恢复,数据库thread记录存在 | 会话状态/缓存错位 | 前端修复缓存逻辑 |
| 新建对话正常,仅老会话报错 | 老thread与当前会话上下文解绑 | 登出重登临时规避 |
| 所有会话全部报thread not found | token整体失效或账号会话异常 | 重登录 |
| 重登录依旧报thread not found | thread真的被删除,DB无记录 | 数据恢复 |
关键点:如果登出重登录修复,99%不是数据库数据丢失,是状态映射问题,不是业务数据损坏。
临时应急方案(用户侧)
- 优先:退出账号,清除客户端本地缓存,重新登录(本次验证有效方案);
- 网页端:无痕模式打开,登录账号验证会话是否可用;
- 不要单纯刷新页面,很多应用 indexDB 不会被普通刷新清空,刷新解决不了该类问题。
工程侧根治方案(开发视角)
如果你在维护基于 Codex / 类似AI会话SDK的应用,需要在代码层面规避该bug。
1. token刷新后,强制刷新会话上下文
access_token 刷新回调中,重新拉取用户会话列表,同步更新内存与本地存储的 thread 映射,不要继续使用旧内存实例。
2. 捕获 thread not found 错误码,做自动容错
接口捕获该特定错误:
- 不要直接抛给用户一个粗暴报错;
- 自动重新拉取会话列表;
- 检测 thread 是否存在于用户会话集合;
- 存在:重新绑定上下文,重试一次请求;
- 不存在:提示会话已失效。
伪代码示例:
js
try {
await sendMessage(threadId, content)
} catch(err) {
if (err.code === 'thread_not_found') {
// 重新拉取该用户全部threads
const threads = await fetchUserThreads()
const exist = threads.some(t=>t.id === threadId)
if(exist){
// thread实际存在,上下文错位,重新初始化会话,重试
reInitSessionContext()
return retrySend()
}else{
alert("该会话已不存在")
}
}
}
3. 登出逻辑务必完整清空存储
登出的时候,不仅清除token,还需要清空 indexDB / localStorage 里面缓存的 thread 上下文、会话实例状态,避免残留脏数据下次登录复现问题。
4. 后端增加日志埋点
打印日志:userId、threadId、是否db存在thread、内存缓存是否命中。
方便区分:是真数据丢失,还是仅仅会话映射缓存失效。
容易踩坑误区
- ❌ 看到
thread not found,就判定会话被删除,去做数据恢复。
登出重登恢复,说明数据还在,不需要恢复数据库。
- ❌ 简单页面刷新当做修复手段。
indexDB/localStorage脏缓存还保留,刷新无法清除,问题依旧复现。
- ❌ 不做错误捕获,直接把原始后端错误抛给终端用户,用户体验差。
总结
thread not found 不等于会话被删除。
本次遇到案例是前端缓存、token刷新、后端会话映射缓存不同步导致的经典分布式会话bug。
用户端应急:退出登录重新登录。
开发端:增加错误捕获、token刷新后重建会话上下文、登出彻底清空本地存储,避免该问题反复出现。