Skip to content

[bug] Codex 会话文件被重编码后,增量扫描重放同一消息 ID 触发幂等 409,「同步新增」永久报「扫描失败」 #374

Description

@QDcvd

现象(Symptoms)

  • 对 Agent 源 Codex 执行扫描,界面一直报「扫描失败」。
  • memory.log 每次扫描出现 6~12 条:"statusCode":409,"error":"conflict","message":"idempotency key reused with different request body"
  • 扫描源水位(latest_seen_created_at)永久停在失败前的值,不再推进;之后每次重扫必失败(死循环)。
  • 手动把水位推进到「最新」可临时恢复一次;但 Codex 对已导入过的会话文件再次改写,下一轮扫描立刻复发。

环境(Environment)

  • Memmy 桌面端 1.1.1(Windows x64;最新 v1.1.2 未见修复,main 分支亦未修复)
  • 扫描源:Codex(builtin),数据目录 C:\Users\user.codex\sessions(JSONL rollout)
  • 复现会话:rollout-2026-09-07T00-54-21-01a077a4-….jsonl(约 8.4MB,消息 >1100 条)

复现步骤(Steps to reproduce)

  1. 用 Codex 开启长会话并持续使用数小时。
  2. Memmy 首次增量扫描成功导入部分消息(实例:导入至消息 :964,checkpoint 07:18:50Z)。
  3. Codex 继续使用/对该 rollout 文件重编码改写(mtime 更新、已导入消息正文序列化变化、追加新消息)。
  4. 再次触发「扫描 Codex」。
  5. 失败:扫描器判定会话「内容修订」整场重放,已导入的同一消息 ID 以不同正文重新提交 → memory/add 幂等层拒绝 → 409 → 扫描整体失败。

预期行为(Expected)

  • 增量扫描应正确处理「会话文件被改写」:按 checkpoint 增量跳过已导入部分只导新增;或以更新语义重放(更新既有记忆)而非冲突失败。
  • 个别消息冲突不应导致整场失败、水位永不推进(避免永久死循环)。

实际行为(Actual)

  • 扫描器检测内容修订后整场重放全部消息(含已导入同一 messageId),memory/add 返回 409,扫描报错。
  • agent-source-service 水位仅在整场零错误时推进,一条 409 即永久卡死 → 每小时/每次手动扫描重复失败。
  • 用户只能手工改库推进水位临时跳过,代价是该会话历史不再进入记忆。

根因分析(Root cause)

  1. Codex 会重写/重编码已导入的会话文件:同一 messageId 的正文序列化发生变化(实证:成功导入后文件被改写,mtime 更新、内容哈希变化)。
  2. 扫描器对「内容修订」会话整场重放:findContentRevisedConversationIds() 判定「最新消息游标==checkpoint 但 content_hash 不同」→ replaySeenConversationIds 整场重放。
  3. 内存服务幂等层恰好拒绝该重放:memory-service.ts 中 idempotent() 以 key=operation:adapterId:requestId、requestHash=stableHash({operation,fingerprint}) 判重,同 key 不同指纹直接抛 409。

即:扫描器主动检测并重放「内容修订」会话,但 memory/add 幂等层恰恰禁止「同 key 换正文」——重放路径架构上永远走不通;再叠加「水位只在零错误时推进」,形成永久死循环。

影响(Impact)

  • 任何「已导入过、之后又被 Codex 改写」的会话都会让 Codex 源扫描永久失败。
  • Codex 是活跃使用的 Agent,长会话重编码是常态,高频复发(本机 09-06~09-08 连续三天,数十次 409)。
  • 用户无法通过界面恢复,只能手工改库推进水位(有数据丢失风险)。

相关 issue(Related)

附:关键代码位置(main,2026-09-08 仍未修复)

  • Memory/src/service/memory-service.ts → idempotent()
  • App/backend/src/services/agent-source-service.ts → findContentRevisedConversationIds() + replaySeenConversationIds;水位仅 input.final && stats.errors.length===0 时推进
  • App/backend/src/adapters/outbound/agent-source/codex/adapter.ts + conversation-window.ts → 整场会话窗口语义

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions