{"record":{"id":"526748112b254bf1","repo":"mongodb/node-mongodb-native","slug":"changestream-is-closed","errorCode":null,"errorMessage":"ChangeStream is closed","messagePattern":"ChangeStream is closed","errorType":"exception","errorClass":"MongoChangeStreamError","httpStatus":null,"severity":"error","filePath":"src/change_stream.ts","lineNumber":870,"sourceCode":"    const cursor = this.cursor;\n    try {\n      await cursor.close();\n    } finally {\n      this._endStream();\n    }\n  }\n\n  /**\n   * Return a modified Readable stream including a possible transform method.\n   *\n   * NOTE: When using a Stream to process change stream events, the stream will\n   * NOT automatically resume in the case a resumable error is encountered.\n   *\n   * @throws MongoChangeStreamError if the underlying cursor or the change stream is closed\n   */\n  stream(): Readable & AsyncIterable<TChange> {\n    if (this.closed) {\n      throw new MongoChangeStreamError(CHANGESTREAM_CLOSED_ERROR);\n    }\n\n    return this.cursor.stream();\n  }\n\n  /** @internal */\n  private _setIsEmitter(): void {\n    if (this.mode === 'iterator') {\n      // TODO(NODE-3485): Replace with MongoChangeStreamModeError\n      throw new MongoAPIError(\n        'ChangeStream cannot be used as an EventEmitter after being used as an iterator'\n      );\n    }\n    this.mode = 'emitter';\n  }\n\n  /** @internal */\n  private _setIsIterator(): void {","sourceCodeStart":852,"sourceCodeEnd":888,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/change_stream.ts#L852-L888","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check changeStream.closed before calling stream() and create a new change stream if needed","Do not mix for-await-of iteration with stream() on the same ChangeStream instance","Create a fresh change stream via collection.watch() if you need to resume after closure"],"exampleFix":"// before\nconst stream = changeStream.stream();\n// ... later after close ...\nconst stream2 = changeStream.stream(); // throws\n\n// after\nif (changeStream.closed) {\n  changeStream = collection.watch(pipeline);\n}\nconst stream2 = changeStream.stream();","handlingStrategy":"validation","validationCode":"// Before calling stream()\nif (changeStream.closed) {\n  // Recreate the change stream if needed\n  changeStream = collection.watch(pipeline, options);\n}\nconst readable = changeStream.stream();","typeGuard":null,"tryCatchPattern":"try {\n  const readable = changeStream.stream();\n} catch (error) {\n  if (error instanceof MongoChangeStreamError && error.message === 'ChangeStream is closed') {\n    changeStream = collection.watch(pipeline, options);\n    readable = changeStream.stream();\n  }\n}","preventionTips":["Always check changeStream.closed before calling stream()","Do not reuse a closed ChangeStream instance; create a new one via watch()","Track the lifecycle of your change streams explicitly in application code"],"tags":["change-stream","lifecycle","streaming"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}