mongodb/node-mongodb-native · error · MongoRuntimeError
Unexpected null serverSession for an ended implicit session
Error message
Unexpected null serverSession for an ended implicit session
What it means
Thrown by the `serverSession` getter when an implicit (non-explicit) session that has already ended is accessed again (sessions.ts:220). Implicit sessions are created internally per-operation and ended automatically; accessing the server session after `end()` has run means the session is no longer usable.
Source
Thrown at src/sessions.ts:221
this.operationTime = undefined;
this.owner = options.owner;
this.defaultTransactionOptions = { ...options.defaultTransactionOptions };
this.transaction = new Transaction();
}
/** The server id associated with this session */
get id(): ServerSessionId | undefined {
return this.serverSession?.id;
}
get serverSession(): ServerSession {
let serverSession = this._serverSession;
if (serverSession == null) {
if (this.explicit) {
throw new MongoRuntimeError('Unexpected null serverSession for an explicit session');
}
if (this.hasEnded) {
throw new MongoRuntimeError('Unexpected null serverSession for an ended implicit session');
}
serverSession = this.sessionPool.acquire();
this._serverSession = serverSession;
}
return serverSession;
}
get loadBalanced(): boolean {
return this.client.topology?.description.type === TopologyType.LoadBalanced;
}
/** @internal */
pin(conn: Connection): void {
if (this.pinnedConnection) {
throw TypeError('Cannot pin multiple connections to the same session');
}
this.pinnedConnection = conn;View on GitHub (pinned to 3366c21a63)
Solutions
- Do not retain or reuse implicit sessions; let the driver manage their lifecycle.
- If you need a long-lived session, create an explicit one with `client.startSession()` and end it yourself when done.
- Ensure cursors are fully consumed or closed before their session ends.
Example fix
// before
const cursor = coll.find({}, { session: implicitSession });
implicitSession.endSession();
await cursor.next(); // throws
// after
await cursor.next();
implicitSession.endSession(); Defensive patterns
Strategy: validation
Validate before calling
function useImplicitSession(session, fn) {
if (session.hasEnded) throw new Error('session has ended; obtain a new one');
return fn();
} Type guard
function isSessionUsable(session) {
return session != null && !session.hasEnded;
} Prevention
- Do not retain implicit sessions across operations.
- Fully consume or close cursors before ending their session.
- Use explicit sessions when you need control over the lifecycle.
When it happens
Trigger: Using an implicit session (e.g. one attached to a cursor) after it was ended; re-running an operation on a cursor whose session was cleaned up; holding a reference to an implicit session past its lifecycle.
Common situations: Iterating a cursor after its owning implicit session was ended; sharing an implicit session handle across async boundaries that outlive the operation; manual `endSession()` on an implicit session followed by further use.
Related errors
- This batch has already been executed, create new batch to ex
- ChangeStream is closed
- Current topology does not support sessions
- Cursor is exhausted
- Cannot rewind cursor that does not own its timeout context.
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/9605308f4cf45512.json.
Report an issue: GitHub.