{"record":{"id":"87d654e55906092c","repo":"mongodb/node-mongodb-native","slug":"a-change-stream-document-has-been-received-that-la","errorCode":null,"errorMessage":"A change stream document has been received that lacks a resume token (_id).","messagePattern":"A change stream document has been received that lacks a resume token \\(_id\\)\\.","errorType":"exception","errorClass":"MongoChangeStreamError","httpStatus":null,"severity":"error","filePath":"src/change_stream.ts","lineNumber":997,"sourceCode":"    this.cursorStream?.destroy();\n    this.cursorStream = undefined;\n  }\n\n  /** @internal */\n  private _processChange(change: TChange | null): TChange {\n    if (this.isClosed) {\n      // TODO(NODE-3485): Replace with MongoChangeStreamClosedError\n      throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);\n    }\n\n    // a null change means the cursor has been notified, implicitly closing the change stream\n    if (change == null) {\n      // TODO(NODE-3485): Replace with MongoChangeStreamClosedError\n      throw new MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR);\n    }\n\n    if (change && !change._id) {\n      throw new MongoChangeStreamError(NO_RESUME_TOKEN_ERROR);\n    }\n\n    // cache the resume token\n    this.cursor.cacheResumeToken(change._id);\n\n    // wipe the startAtOperationTime if there was one so that there won't be a conflict\n    // between resumeToken and startAtOperationTime if we need to reconnect the cursor\n    this.options.startAtOperationTime = undefined;\n\n    return change;\n  }\n\n  /** @internal */\n  private _processErrorStreamMode(changeStreamError: AnyError, cursorInitialized: boolean) {\n    // If the change stream has been closed explicitly, do not process error.\n    if (this.isClosed) return;\n\n    if (","sourceCodeStart":979,"sourceCodeEnd":1015,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/change_stream.ts#L979-L1015","documentation":"Thrown by _processChange() when a change document from the server does not contain an _id field (the resume token). Every change stream notification must include a resume token so the driver can resume after interruptions. Receiving a document without one is a protocol violation that makes resumption impossible. This is a MongoChangeStreamError.","triggerScenarios":"A custom aggregation pipeline stage (e.g., $project, $unset, $replaceRoot) that removes or renames the _id field from change documents; a server bug or version incompatibility that omits the resume token; network-level data corruption.","commonSituations":"Adding a $project stage to the change stream pipeline that excludes _id; adding a $replaceRoot stage that changes the document structure; using a pipeline that transforms the change document shape before it reaches the driver's resume-token logic.","solutions":["Review your change stream pipeline stages and ensure none remove or rename the _id field from change documents","If you need to transform documents, do so in application code after receiving the change, not in the pipeline","Add _id: 1 to any $project stage in the change stream pipeline"],"exampleFix":"// before\nconst stream = collection.watch([\n  { $project: { 'fullDocument._id': 1, operationType: 1 } } // drops top-level _id\n]);\n\n// after\nconst stream = collection.watch([\n  { $project: { _id: 1, 'fullDocument._id': 1, operationType: 1 } }\n]);","handlingStrategy":"validation","validationCode":"// Validate pipeline does not strip _id before starting the change stream\nconst FORBIDDEN_STAGES = ['$replaceRoot', '$replaceWith'];\nconst hasProjectWithoutId = pipeline.some(\n  stage => stage.$project && stage.$project._id === 0\n);\nif (hasProjectWithoutId) {\n  throw new Error('Change stream pipeline must not remove the _id resume token field');\n}","typeGuard":null,"tryCatchPattern":"try {\n  for await (const change of changeStream) {\n    processChange(change);\n  }\n} catch (error) {\n  if (error instanceof MongoChangeStreamError && error.message.includes('resume token')) {\n    // Pipeline is stripping _id; fix the pipeline and restart\n    console.error('Fix pipeline to preserve _id field on change documents');\n  }\n}","preventionTips":["Never use $project with _id: 0 in a change stream pipeline","Avoid $replaceRoot or $replaceWith stages that change the document shape","Perform transformations in application code, not in the change stream pipeline","If projecting fields, always include _id: 1"],"tags":["change-stream","aggregation-pipeline","resume-token"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}