mongodb/node-mongodb-native · error · MongoChangeStreamError

ChangeStream is closed

Error message

ChangeStream is closed

What it means

Thrown by ChangeStream.stream() when the change stream has already been closed (via close() or because the underlying cursor closed). The stream() method returns a Readable for low-level streaming access, but once closed no new stream can be obtained. This is a MongoChangeStreamError.

Solutions

  1. Check changeStream.closed before calling stream() and create a new change stream if needed
  2. Do not mix for-await-of iteration with stream() on the same ChangeStream instance
  3. Create a fresh change stream via collection.watch() if you need to resume after closure

Example fix

// before
const stream = changeStream.stream();
// ... later after close ...
const stream2 = changeStream.stream(); // throws

// after
if (changeStream.closed) {
  changeStream = collection.watch(pipeline);
}
const stream2 = changeStream.stream();
Defensive patterns

Strategy: validation

Validate before calling

// Before calling stream()
if (changeStream.closed) {
  // Recreate the change stream if needed
  changeStream = collection.watch(pipeline, options);
}
const readable = changeStream.stream();

Try / catch

try {
  const readable = changeStream.stream();
} catch (error) {
  if (error instanceof MongoChangeStreamError && error.message === 'ChangeStream is closed') {
    changeStream = collection.watch(pipeline, options);
    readable = changeStream.stream();
  }
}

Prevention

When it happens

Trigger: Calling changeStream.stream() after changeStream.close() has been invoked, or after the cursor was closed due to an unrecoverable error or an invalidate event from the server.

Common situations: Code that re-obtains a stream after error handling closes the change stream; using for-await-of (which calls close() in its finally block) and then attempting stream(); the server sent an invalidate event that internally closed the cursor.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/526748112b254bf1. Report an issue: GitHub.

Appendix: source

Thrown at src/change_stream.ts:870

    const cursor = this.cursor;
    try {
      await cursor.close();
    } finally {
      this._endStream();
    }
  }

  /**
   * Return a modified Readable stream including a possible transform method.
   *
   * NOTE: When using a Stream to process change stream events, the stream will
   * NOT automatically resume in the case a resumable error is encountered.
   *
   * @throws MongoChangeStreamError if the underlying cursor or the change stream is closed
   */
  stream(): Readable & AsyncIterable<TChange> {
    if (this.closed) {
      throw new MongoChangeStreamError(CHANGESTREAM_CLOSED_ERROR);
    }

    return this.cursor.stream();
  }

  /** @internal */
  private _setIsEmitter(): void {
    if (this.mode === 'iterator') {
      // TODO(NODE-3485): Replace with MongoChangeStreamModeError
      throw new MongoAPIError(
        'ChangeStream cannot be used as an EventEmitter after being used as an iterator'
      );
    }
    this.mode = 'emitter';
  }

  /** @internal */
  private _setIsIterator(): void {

View on GitHub (pinned to dce7939f86)