mongodb/node-mongodb-native · error · MongoRuntimeError
Unexpected null session. A cursor creating command should…
Error message
Unexpected null session. A cursor creating command should have set this
What it means
Thrown by RunCommandCursor.getMore when this.session is null at the time a getMore is sent. A cursor that has been initialized must have an implicit or explicit session attached by AbstractCursor; a null session here means the cursor was never successfully initialized, was closed, or the session was cleared by an external path. This is a MongoRuntimeError indicating driver-internal state corruption rather than user misuse.
Solutions
- Ensure the cursor is iterated normally via for-await or .next() so _initialize attaches the session.
- Do not reuse a cursor after close(); create a fresh one via db.runCursorCommand.
- Upgrade the driver to the latest patch release if hitting this on an older version, as it usually indicates a fixed internal bug.
- Avoid sharing a single cursor across concurrent consumers.
Example fix
// before
const cursor = db.runCursorCommand(cmd);
cursor.close();
await cursor.next(); // session is null
// after
const cursor = db.runCursorCommand(cmd);
for await (const doc of cursor) {
if (done) { await cursor.close(); break; }
handle(doc);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await cursor.next();
} catch (e) {
if (e instanceof MongoRuntimeError && /Unexpected null session/.test(e.message)) {
// cursor is not in an initialized state: recreate it
cursor = db.runCursorCommand(cmd, opts);
} else throw e;
} Prevention
- Never reuse a cursor after close(); create a new one via db.runCursorCommand.
- Iterate cursors with for-await so _initialize runs exactly once and attaches the session.
- Do not share a cursor across concurrent consumers; serialize access or open one per consumer.
- Upgrade to the latest driver patch if hitting this on an older version, as it usually signals an internal lifecycle bug.
When it happens
Trigger: Calling cursor.next() / iterating a RunCommandCursor whose _initialize never ran (e.g. bypassed via internal API); iterating a cursor after it was closed and its session released; concurrent close() racing with getMore().
Common situations: Custom cursor wrappers that skip AbstractCursor initialization; bugs in older driver versions around session cleanup; concurrent consumers racing close() and next() on the same cursor.
Related errors
- batchSize must be configured on the command document…
- Cannot abort a stream that has already completed
- Cannot call abort() on a stream twice
- Cannot call abortTransaction after calling commitTransaction
- Cannot call abortTransaction twice
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/b02f5e749d2c6261.
Report an issue: GitHub.
Appendix: source
Thrown at src/cursor/run_command_cursor.ts:164
const operation = new RunCursorCommandOperation(this.db.s.namespace, this.command, {
...this.cursorOptions,
session: session,
readPreference: this.cursorOptions.readPreference
});
const response = await executeOperation(this.client, operation, this.timeoutContext);
return {
server: operation.server,
session,
response
};
}
/** @internal */
override async getMore(): Promise<CursorResponse> {
if (!this.session) {
throw new MongoRuntimeError(
'Unexpected null session. A cursor creating command should have set this'
);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const getMoreOperation = new GetMoreOperation(this.namespace, this.id!, this.server!, {
...this.cursorOptions,
session: this.session,
...this.getMoreOptions
});
return await executeOperation(this.client, getMoreOperation, this.timeoutContext);
}
}
View on GitHub (pinned to dce7939f86)