{"id":"11212851bc91284b","repo":"mongodb/node-mongodb-native","slug":"use-of-expired-sessions-is-not-permitted","errorCode":null,"errorMessage":"Use of expired sessions is not permitted","messagePattern":"Use of expired sessions is not permitted","errorType":"exception","errorClass":"MongoExpiredSessionError","httpStatus":null,"severity":"error","filePath":"src/operations/execute_operation.ts","lineNumber":88,"sourceCode":"    // TODO(NODE-3483): Extend MongoRuntimeError\n    throw new MongoRuntimeError('This method requires a valid operation instance');\n  }\n\n  const topology =\n    client.topology == null\n      ? await abortable(autoConnect(client), operation.options)\n      : client.topology;\n\n  // The driver sessions spec mandates that we implicitly create sessions for operations\n  // that are not explicitly provided with a session.\n  let session = operation.session;\n  let owner: symbol | undefined;\n\n  if (session == null) {\n    owner = Symbol();\n    session = client.startSession({ owner, explicit: false });\n  } else if (session.hasEnded) {\n    throw new MongoExpiredSessionError('Use of expired sessions is not permitted');\n  } else if (\n    session.snapshotEnabled &&\n    maxWireVersion(topology) < MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION\n  ) {\n    throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later');\n  } else if (session.client !== client) {\n    throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');\n  }\n\n  operation.session ??= session;\n\n  const readPreference = operation.readPreference ?? ReadPreference.primary;\n  const inTransaction = !!session?.inTransaction();\n\n  const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);\n\n  if (\n    inTransaction &&","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/operations/execute_operation.ts#L70-L106","documentation":"When an explicit ClientSession is passed to an operation, executeOperation (src/operations/execute_operation.ts:88) checks session.hasEnded. If the session has already ended (via endSession, commit with no continuation, or CSOT timeout), a MongoExpiredSessionError is thrown. Sessions are single-use resources and cannot be recycled after ending.","triggerScenarios":"Passing a session to an operation after calling session.endSession(), after session.withTransaction has completed, or after a CSOT (Client Server Operation Timeout) has expired the session.","commonSituations":"Manual session lifecycle management where endSession() is called too early, reusing a session across multiple withTransaction blocks, or a timeoutMS that causes the session to expire mid-workflow.","solutions":["Do not reuse a session after endSession(); start a fresh session with client.startSession() for each logical unit of work.","If using withTransaction, let the helper manage the session lifecycle and do not pass the same session to subsequent operations.","If using timeoutMS/CSOT, increase the timeout or restructure the workload so the session does not expire before all operations complete.","Check session.hasEnded before using it if you are unsure of its state."],"exampleFix":"// before\nconst session = client.startSession();\nawait session.endSession();\nawait collection.insertOne({ a: 1 }, { session }); // throws\n\n// after\nconst session = client.startSession();\ntry {\n  await collection.insertOne({ a: 1 }, { session });\n} finally {\n  await session.endSession();\n}","handlingStrategy":"validation","validationCode":"function isSessionUsable(session: ClientSession): boolean {\n  return !session.hasEnded;\n}\n// usage:\nif (!isSessionUsable(session)) {\n  session = client.startSession();\n}","typeGuard":"const isUsableSession = (s: ClientSession): boolean => !s.hasEnded;","tryCatchPattern":"try {\n  await collection.insertOne(doc, { session });\n} catch (err) {\n  if (err instanceof MongoExpiredSessionError) {\n    session = client.startSession();\n    // retry with new session if appropriate\n  } else throw err;\n}","preventionTips":["Always end sessions in a finally block after the logical unit of work.","Let withTransaction manage the session lifecycle; do not reuse the session afterward.","Avoid setting timeoutMS too low when long sessions are needed."],"tags":["session","lifecycle","timeout","csot"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}