{"record":{"id":"ba8c765134ea55fa","repo":"Automattic/mongoose","slug":"cannot-call-addlistener-on-errored-changestream","errorCode":null,"errorMessage":"Cannot call addListener() on errored ChangeStream","messagePattern":"Cannot call addListener\\(\\) on errored ChangeStream","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/cursor/changeStream.js","lineNumber":179,"sourceCode":"\n    if (this.driverChangeStream != null) {\n      return this.driverChangeStream.next(cb);\n    }\n\n    return this.$driverChangeStreamPromise.then(\n      () => this.driverChangeStream.next(cb),\n      err => {\n        if (cb != null) {\n          return cb(err);\n        }\n        throw err;\n      }\n    );\n  }\n\n  addListener(event, handler) {\n    if (this.errored) {\n      throw new MongooseError('Cannot call addListener() on errored ChangeStream');\n    }\n    this._bindEvents();\n    return super.addListener(event, handler);\n  }\n\n  on(event, handler) {\n    if (this.errored) {\n      throw new MongooseError('Cannot call on() on errored ChangeStream');\n    }\n    this._bindEvents();\n    return super.on(event, handler);\n  }\n\n  once(event, handler) {\n    if (this.errored) {\n      throw new MongooseError('Cannot call once() on errored ChangeStream');\n    }\n    this._bindEvents();","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/cursor/changeStream.js#L161-L197","documentation":"ChangeStream marks itself errored when the underlying driver stream fails fatally. addListener() on an errored stream throws immediately because binding new handlers to a dead stream would silently never fire, so Mongoose rejects the call.","triggerScenarios":"Calling stream.addListener('change', fn) (or any event) after the stream emitted a fatal 'error' event — typically in reconnection code that re-attaches handlers to the same stream object.","commonSituations":"Reconnect wrappers that re-register event handlers after a failure; modular code where listener setup races with a stream error during a replica-set failover.","solutions":["On stream 'error', close() the errored stream and create a fresh one before attaching listeners","Guard registration: if (stream.errored || stream.closed) stream = await rebuildStream(); then addListener()","Prefer on()/once() on a newly created stream rather than reusing stream instances after errors"],"exampleFix":"// before\nfunction attach(stream) {\n  stream.addListener('change', sync); // throws if the stream already errored\n}\n\n// after\nfunction attach(stream) {\n  if (stream.errored || stream.closed) stream = MyModel.watch(pipeline, opts);\n  stream.addListener('change', sync);\n}","handlingStrategy":"validation","validationCode":"function attach(stream, event, handler) {\n  if (stream.errored || stream.closed) stream = makeStream(); // fresh stream first\n  stream.addListener(event, handler);\n  return stream;\n}","typeGuard":"const isUsableChangeStream = (s) =>\n  s != null && typeof s.on === 'function' && !s.errored && !s.closed;","tryCatchPattern":null,"preventionTips":["Register all listeners right after creating the stream, before errors can occur","On 'error', rebuild the stream and re-attach listeners to the new instance","Keep listener registration in one place so it cannot race with stream death"],"tags":["change-stream","addlistener","error-recovery","mongoose"],"backgroundTag":"change-stream-resume-failed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}