mongodb/node-mongodb-native · critical · MongoRuntimeError
Unexpected null cursor id. A cursor creating command should…
Error message
Unexpected null cursor id. A cursor creating command should have set this
What it means
Thrown as a MongoRuntimeError from getMore() when cursorId is null at the time a getMore is requested. cursorId is set from the server's response during cursorInit(); reaching getMore() with it still null means the cursor was never successfully initialized (the initial command did not run or did not store a response). This is a driver-internal invariant violation, not a user input error: it indicates either a bug in the driver or that cursor initialization was bypassed/short-circuited. getMore() is marked @internal, so direct user invocation is unsupported.
Solutions
- Report the issue to the driver maintainers with a reproduction, since this is an invariant the driver itself guarantees.
- Ensure you only iterate via the public API (next(), hasNext(), for-await, toArray()) so initialization is always driven by fetchBatch().
- If subclassing, implement _initialize() to return a valid InitialCursorResponse with a non-null response.id.
- Upgrade to the latest driver patch release in case the bug is already fixed.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await cursor.next();
} catch (err) {
if (err instanceof MongoRuntimeError && /null cursor id/.test(err.message)) {
// driver invariant violated: report and recreate the cursor
cursor = collection.find(filter, opts);
} else {
throw err;
}
} Prevention
- Do not call the @internal getMore() directly; use the public iteration API.
- Implement _initialize() correctly if subclassing (return a response with an id).
- Keep the driver up to date; report suspected invariant violations with a reproduction.
When it happens
Trigger: Driver internals call getMore() before cursorInit() completed; a subclass overrides _initialize() to return without a proper response; reflection or test code calls the internal getMore() directly on a never-run cursor.
Common situations: Hitting a genuine driver bug where initialization raced with iteration; instrumenting or monkey-patching cursor internals; an unhandled rejection during _initialize() leaving the cursor half-initialized.
Related errors
- Unexpected null selectedServer. A cursor creating command…
- Unexpected null session. A cursor creating command should…
- A collection name must be determined before getMore
- A collection name must be determined before killCursors
- Argument for maxTimeMS must be a number
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/ef37f36d28756c81.
Report an issue: GitHub.
Appendix: source
Thrown at src/cursor/abstract_cursor.ts:861
this.cursorSession = null;
}
}
/**
* Returns a new uninitialized copy of this cursor, with options matching those that have been set on the current instance
*/
abstract clone(): AbstractCursor<TSchema>;
/** @internal */
protected abstract _initialize(
session: ClientSession | undefined
): Promise<InitialCursorResponse>;
/** @internal */
async getMore(): Promise<CursorResponse> {
if (this.cursorId == null) {
throw new MongoRuntimeError(
'Unexpected null cursor id. A cursor creating command should have set this'
);
}
if (this.selectedServer == null) {
throw new MongoRuntimeError(
'Unexpected null selectedServer. A cursor creating command should have set this'
);
}
if (this.cursorSession == null) {
throw new MongoRuntimeError(
'Unexpected null session. A cursor creating command should have set this'
);
}
const getMoreOptions = {
...this.cursorOptions,
session: this.cursorSession,
batchSize: this.cursorOptions.batchSizeView on GitHub (pinned to dce7939f86)