mongodb/node-mongodb-native · error · MongoExpiredSessionError
Use of expired sessions is not permitted
Error message
Use of expired sessions is not permitted
What it means
Thrown when an operation is executed with a ClientSession that has already been ended (session.hasEnded is true). Sessions are single-use: once endSession() runs (explicitly or via withSession's finally block), the session cannot be reused for further operations.
Solutions
- Do not escape the session from withSession — perform all work inside the callback.
- If managing sessions manually, never reuse after endSession(); create a new session with startSession() instead.
- Check session.hasEnded before passing to an operation as a defensive guard.
Example fix
// before
let saved;
await client.withSession(session => { saved = session; });
await collection.insertOne({}, { session: saved }); // session has ended
// after
await client.withSession(async session => {
await collection.insertOne({}, { session });
}); Defensive patterns
Strategy: validation
Validate before calling
if (session.hasEnded) {
throw new Error('Cannot reuse an ended session');
}
await collection.findOne({}, { session }); Type guard
const isLiveSession = (s: { hasEnded?: boolean }): boolean =>
s != null && s.hasEnded !== true; Try / catch
try {
await collection.findOne({}, { session });
} catch (e) {
if (/expired sessions/.test(e.message)) {
session = client.startSession();
// retry once with a fresh session
}
} Prevention
- Do all session work inside withSession's callback.
- Never cache or escape sessions across async boundaries.
When it happens
Trigger: Keeping a reference to a session from inside client.withSession and using it after the callback returns; calling session.endSession() manually and then passing the same session to another operation; caching sessions across requests.
Common situations: Web handlers that store the session on the request object and reuse it after the response; helpers that call endSession too early in a pipeline; misusing withSession by escaping the session via a closure.
Related errors
- Cannot call abortTransaction after calling commitTransaction
- Cannot call abortTransaction twice
- Cannot call commitTransaction after calling abortTransaction
- Client must be connected before running operations
- ClientSession must be from the same MongoClient
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/11212851bc91284b.
Report an issue: GitHub.
Appendix: source
Thrown at src/operations/execute_operation.ts:88
// TODO(NODE-3483): Extend MongoRuntimeError
throw new MongoRuntimeError('This method requires a valid operation instance');
}
const topology =
client.topology == null
? await abortable(autoConnect(client), operation.options)
: client.topology;
// The driver sessions spec mandates that we implicitly create sessions for operations
// that are not explicitly provided with a session.
let session = operation.session;
let owner: symbol | undefined;
if (session == null) {
owner = Symbol();
session = client.startSession({ owner, explicit: false });
} else if (session.hasEnded) {
throw new MongoExpiredSessionError('Use of expired sessions is not permitted');
} else if (
session.snapshotEnabled &&
maxWireVersion(topology) < MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION
) {
throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later');
} else if (session.client !== client) {
throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
}
operation.session ??= session;
const readPreference = operation.readPreference ?? ReadPreference.primary;
const inTransaction = !!session?.inTransaction();
const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
if (
inTransaction &&View on GitHub (pinned to dce7939f86)