mongodb/node-mongodb-native · error · MongoChangeStreamError
A change stream document has been received that lacks a…
Error message
A change stream document has been received that lacks a resume token (_id).
What it means
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.
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
Example fix
// before
const stream = collection.watch([
{ $project: { 'fullDocument._id': 1, operationType: 1 } } // drops top-level _id
]);
// after
const stream = collection.watch([
{ $project: { _id: 1, 'fullDocument._id': 1, operationType: 1 } }
]); Defensive patterns
Strategy: validation
Validate before calling
// Validate pipeline does not strip _id before starting the change stream
const FORBIDDEN_STAGES = ['$replaceRoot', '$replaceWith'];
const hasProjectWithoutId = pipeline.some(
stage => stage.$project && stage.$project._id === 0
);
if (hasProjectWithoutId) {
throw new Error('Change stream pipeline must not remove the _id resume token field');
} Try / catch
try {
for await (const change of changeStream) {
processChange(change);
}
} catch (error) {
if (error instanceof MongoChangeStreamError && error.message.includes('resume token')) {
// Pipeline is stripping _id; fix the pipeline and restart
console.error('Fix pipeline to preserve _id field on change documents');
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ChangeStream cannot be used as an EventEmitter after being…
- ChangeStream cannot be used as an iterator after being used…
- ChangeStream is closed
- Parent provided to ChangeStream constructor must be an…
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/87d654e55906092c.
Report an issue: GitHub.
Appendix: source
Thrown at src/change_stream.ts:997
this.cursorStream?.destroy();
this.cursorStream = undefined;
}
/** @internal */
private _processChange(change: TChange | null): TChange {
if (this.isClosed) {
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
throw new MongoAPIError(CHANGESTREAM_CLOSED_ERROR);
}
// a null change means the cursor has been notified, implicitly closing the change stream
if (change == null) {
// TODO(NODE-3485): Replace with MongoChangeStreamClosedError
throw new MongoRuntimeError(CHANGESTREAM_CLOSED_ERROR);
}
if (change && !change._id) {
throw new MongoChangeStreamError(NO_RESUME_TOKEN_ERROR);
}
// cache the resume token
this.cursor.cacheResumeToken(change._id);
// wipe the startAtOperationTime if there was one so that there won't be a conflict
// between resumeToken and startAtOperationTime if we need to reconnect the cursor
this.options.startAtOperationTime = undefined;
return change;
}
/** @internal */
private _processErrorStreamMode(changeStreamError: AnyError, cursorInitialized: boolean) {
// If the change stream has been closed explicitly, do not process error.
if (this.isClosed) return;
if (View on GitHub (pinned to dce7939f86)