mongodb/node-mongodb-native · error · MongoAPIError
ChangeStream cannot be used as an iterator after being used
Error message
ChangeStream cannot be used as an iterator after being used as an EventEmitter
What it means
Inverse of error 21: _setIsIterator() throws this MongoAPIError when you attempt to consume the ChangeStream with for-await after it has already been used as an EventEmitter (on('change', ...)). The driver tracks mode to prevent the cursor from being driven by two different mechanisms at once.
Source
Thrown at src/change_stream.ts:891
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 {
if (this.mode === 'emitter') {
// TODO(NODE-3485): Replace with MongoChangeStreamModeError
throw new MongoAPIError(
'ChangeStream cannot be used as an iterator after being used as an EventEmitter'
);
}
this.mode = 'iterator';
}
/**
* Create a new change stream cursor based on self's configuration
* @internal
*/
private _createChangeStreamCursor(
options: ChangeStreamOptions | ChangeStreamCursorOptions
): ChangeStreamCursor<TSchema, TChange> {
const changeStreamStageOptions: Document = filterOutOptions(options);
if (this.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
changeStreamStageOptions.allChangesForCluster = true;
}
const pipeline = [{ $changeStream: changeStreamStageOptions }, ...this.pipeline];View on GitHub (pinned to 3366c21a63)
Solutions
- Stop using event listeners on this instance and consume it solely via for-await, or vice versa.
- Spin up a second ChangeStream via collection.watch() dedicated to the iterator consumption.
- Remove the .on('change') registration (and call close() if needed) before creating a new instance to iterate.
Example fix
// before
cs.on('change', handle);
for await (const change of cs) {} // throws
// after
cs.on('change', handle); // emitter mode only Defensive patterns
Strategy: validation
Validate before calling
// Application-level: do not register listeners on instances you intend to iterate.
const MODE = Symbol('mode');
function iterate(cs) {
if (cs.listenerCount('change') > 0) throw new Error('already in emitter mode');
} Type guard
// No public mode field; model it in your own wrapper.
interface ManagedChangeStream { mode: 'emitter' | 'iterator' | null; } Try / catch
try {
for await (const c of cs) handle(c);
} catch (err) {
if (err instanceof MongoAPIError && /iterator after being used as an EventEmitter/.test(err.message)) {
// open a fresh change stream for iteration
} else throw err;
} Prevention
- Choose for-await OR event listeners up front and do not mix them.
- If a library attaches listeners, give it its own ChangeStream instance.
- Code-review change-stream usage for accidental dual-mode access.
When it happens
Trigger: First registering `changeStream.on('change', cb)` (which starts emitter-mode pumping via _streamEvents), then later running `for await (const change of changeStream)` on the same instance.
Common situations: Layered code where a base module attaches listeners and a caller later tries to iterate; replacing a listener with iteration without removing the listener; integration tests that exercise both APIs on one instance.
Related errors
- ChangeStream cannot be used as an EventEmitter after being u
- A change stream document has been received that lacks a resu
- ChangeStream is closed
- Clone not supported, create a new cursor with db.runCursorCo
- RunCommandCursor does not support readConcern it must be att
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/32ea2ad934675f1c.json.
Report an issue: GitHub.