mongodb/node-mongodb-native · error · MongoCursorExhaustedError
Cursor is exhausted
Error message
Cursor is exhausted
What it means
MongoCursorExhaustedError thrown by cursor.next() when cursorId === Long.ZERO, meaning the server has already signaled that the cursor is fully drained and no more batches are forthcoming. Calling next() on an already-exhausted cursor is a logic error because there is nothing left to fetch.
Source
Thrown at src/cursor/abstract_cursor.ts:553
return true;
}
await this.fetchBatch();
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
} finally {
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION) {
this.timeoutContext?.clear();
}
}
return false;
}
/** Get the next available document from the cursor, returns null if no more documents are available. */
async next(): Promise<TSchema | null> {
this.signal?.throwIfAborted();
if (this.cursorId === Long.ZERO) {
throw new MongoCursorExhaustedError();
}
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION && this.cursorId != null) {
this.timeoutContext?.refresh();
}
try {
do {
const doc = this.documents?.shift(this.deserializationOptions);
if (doc != null) {
if (this.transform != null) return await this.transformDocument(doc);
return doc;
}
await this.fetchBatch();
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
} finally {
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION) {
this.timeoutContext?.clear();View on GitHub (pinned to 3366c21a63)
Solutions
- Stop calling next() once it returns null — that signals exhaustion.
- Use `for await (const doc of cursor)` which handles termination cleanly.
- Track a local `exhausted` flag and skip further next() calls.
Example fix
// before
while (true) { const d = await cursor.next(); use(d); } // throws after drain
// after
for await (const d of cursor) { use(d); } Defensive patterns
Strategy: try-catch
Try / catch
try {
const doc = await cursor.next();
if (doc === null) return; // graceful end
} catch (e) {
if (e instanceof MongoCursorExhaustedError) return;
throw e;
} Prevention
- Prefer for-await-of which terminates cleanly on exhaustion.
- Treat a null return from next() as final — do not call next() again.
- Track a local exhausted flag in manual loops.
When it happens
Trigger: Calling await cursor.next() after iteration completed, after a previous next() returned null, after cursor.close(), or after the server returned a zero cursorId on the initial find/getMore. Common in polling loops that do not break on null.
Common situations: While/for loops that ignore a previous null return and call next() again; reusing a cursor stored in a long-lived object after it drained; race where two consumers drain the same cursor.
Related errors
- Argument "iterator" must be a function
- Cannot rewind cursor that does not own its timeout context.
- Cursor is already initialized
- Unable to iterate cursor with no id
- This batch has already been executed, create new batch to ex
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/7640f1cff3fa88d3.json.
Report an issue: GitHub.