{
  "id": "mongodb/change-stream-resume-token-invalid-for-namespace",
  "signature": "MongoServerError: The resume token for namespace 'mydb.mycoll' is invalid or has been pruned: resumeAfter timestamp too old",
  "signature_zh": "MongoServerError: 命名空间 'mydb.mycoll' 的恢复令牌无效或已被修剪：resumeAfter 时间戳太旧",
  "regex": "resume token.*invalid.*pruned.*resumeAfter timestamp too old",
  "domain": "mongodb",
  "category": "protocol_error",
  "subcategory": null,
  "root_cause": "A change stream's resume token refers to an oplog entry that has been pruned due to the oplog size limit or because the change stream was idle longer than the oplog window (typically 24 hours on Atlas).",
  "root_cause_type": "generic",
  "root_cause_zh": "变更流的恢复令牌引用了已被修剪的操作日志条目，原因可能是操作日志大小限制，或变更流空闲时间超过操作日志窗口（Atlas 上通常为 24 小时）。",
  "versions": [
    {
      "version": "MongoDB 5.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "MongoDB 6.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "MongoDB 7.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "Atlas M10+",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    }
  ],
  "os_specific": {},
  "dead_ends": [
    {
      "action": "",
      "why_fails": "The resume token is already invalid; a new change stream must be started from the current time, losing all events between the old token and now.",
      "fail_rate": 0.9,
      "condition": "",
      "sources": []
    },
    {
      "action": "",
      "why_fails": "The oplog entry is permanently pruned; retrying will always fail.",
      "fail_rate": 1.0,
      "condition": "",
      "sources": []
    },
    {
      "action": "",
      "why_fails": "The error is server-side; no driver can recover a pruned oplog entry.",
      "fail_rate": 0.95,
      "condition": "",
      "sources": []
    }
  ],
  "workarounds": [
    {
      "action": "Start a new change stream from the current time and accept data loss of missed events. In code: `collection.watch([], { resumeAfter: null, startAfter: null })` or use `startAtOperationTime` with a recent timestamp.",
      "success_rate": 0.7,
      "how": "Start a new change stream from the current time and accept data loss of missed events. In code: `collection.watch([], { resumeAfter: null, startAfter: null })` or use `startAtOperationTime` with a recent timestamp.",
      "condition": "",
      "sources": []
    },
    {
      "action": "If using Atlas, increase the oplog window by upgrading cluster tier or enabling long-term oplog retention (if available). For self-managed, increase oplog size: `db.adminCommand({ replSetResizeOplog: 1, size: 102400 })` (in MB).",
      "success_rate": 0.8,
      "how": "If using Atlas, increase the oplog window by upgrading cluster tier or enabling long-term oplog retention (if available). For self-managed, increase oplog size: `db.adminCommand({ replSetResizeOplog: 1, size: 102400 })` (in MB).",
      "condition": "",
      "sources": []
    },
    {
      "action": "Implement a fallback mechanism: store the last processed document ID in an external store (e.g., Redis) and use `startAfter` with the last known _id to catch up, then switch to resume tokens.",
      "success_rate": 0.75,
      "how": "Implement a fallback mechanism: store the last processed document ID in an external store (e.g., Redis) and use `startAfter` with the last known _id to catch up, then switch to resume tokens.",
      "condition": "",
      "sources": []
    }
  ],
  "workarounds_zh": [
    "从当前时间开始新的变更流，接受丢失事件的数据损失。在代码中：`collection.watch([], { resumeAfter: null, startAfter: null })` 或使用带有最近时间戳的 `startAtOperationTime`。",
    "如果使用 Atlas，通过升级集群层级或启用长期操作日志保留（如果可用）来增加操作日志窗口。对于自管理，增加操作日志大小：`db.adminCommand({ replSetResizeOplog: 1, size: 102400 })`（以 MB 为单位）。",
    "实现回退机制：将最后处理的文档 ID 存储在外部存储（例如 Redis）中，并使用 `startAfter` 与最后已知的 _id 来追赶，然后切换回恢复令牌。"
  ],
  "transition_graph": {
    "leads_to": [],
    "preceded_by": [],
    "frequently_confused_with": []
  },
  "official_doc_url": "https://www.mongodb.com/docs/manual/changeStreams/#resume-a-change-stream",
  "official_doc_section": null,
  "error_code": null,
  "verification_tier": "ai_generated",
  "confidence": 0.87,
  "fix_success_rate": 0.7,
  "resolvable": "partial",
  "first_seen": "2023-11-05",
  "last_confirmed": "2024-06-01",
  "last_updated": "2024-06-01",
  "evidence_count": 1,
  "tags": [],
  "locale": "en",
  "aliases": []
}