{"record":{"id":"3d89ded6b683147c","repo":"mongodb/node-mongodb-native","slug":"unexpected-null-session-a-cursor-creating-command","errorCode":null,"errorMessage":"Unexpected null session. A cursor creating command should have set this","messagePattern":"Unexpected null session\\. A cursor creating command should have set this","errorType":"exception","errorClass":"MongoRuntimeError","httpStatus":null,"severity":"critical","filePath":"src/cursor/abstract_cursor.ts","lineNumber":872,"sourceCode":"  protected abstract _initialize(\n    session: ClientSession | undefined\n  ): Promise<InitialCursorResponse>;\n\n  /** @internal */\n  async getMore(): Promise<CursorResponse> {\n    if (this.cursorId == null) {\n      throw new MongoRuntimeError(\n        'Unexpected null cursor id. A cursor creating command should have set this'\n      );\n    }\n    if (this.selectedServer == null) {\n      throw new MongoRuntimeError(\n        'Unexpected null selectedServer. A cursor creating command should have set this'\n      );\n    }\n\n    if (this.cursorSession == null) {\n      throw new MongoRuntimeError(\n        'Unexpected null session. A cursor creating command should have set this'\n      );\n    }\n    const getMoreOptions = {\n      ...this.cursorOptions,\n      session: this.cursorSession,\n      batchSize: this.cursorOptions.batchSize\n    };\n\n    const getMoreOperation = new GetMoreOperation(\n      this.cursorNamespace,\n      this.cursorId,\n      this.selectedServer,\n      getMoreOptions\n    );\n\n    return await executeOperation(this.cursorClient, getMoreOperation, this.timeoutContext);\n  }","sourceCodeStart":854,"sourceCodeEnd":890,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cursor/abstract_cursor.ts#L854-L890","documentation":"Thrown as a MongoRuntimeError from getMore() when cursorSession is null at getMore time. The session is created during cursorInit() (either the user's explicit session or an implicit one with owner=this) and must be reused for every getMore on the same cursor per the cross-driver cursor contract. A null session at getMore time means initialization never ran or the session was cleared (e.g. by a rewind() that ended it) while iteration continued, which is an internal invariant violation. getMore() is @internal.","triggerScenarios":"Driver bug where cursorInit() did not assign cursorSession before fetchBatch() ran getMore(); a subclass returns from _initialize() without ensuring a session; session state lost after a partial cleanup triggered by an earlier error.","commonSituations":"Hitting a driver bug under error paths where cleanup ran but iteration continued; instrumenting session lifecycle; an unhandled rejection in startSession() leaving the cursor without a session.","solutions":["Report the issue with a reproduction and driver version; this is a driver-internal contract.","Use only the public iteration API so session creation is handled by cursorInit().","If providing an explicit session, ensure it remains usable (not ended) for the cursor's lifetime.","Upgrade to the latest patch release."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await cursor.next();\n} catch (err) {\n  if (err instanceof MongoRuntimeError && /null session/.test(err.message)) {\n    // internal invariant: recreate the cursor with a fresh implicit session\n    cursor = collection.find(filter, opts);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Let the driver create and manage implicit sessions; avoid ending an explicit session mid-iteration.","Do not call the @internal getMore() directly.","Report suspected driver bugs; keep the driver version current."],"tags":["cursor","internal-invariant","runtime-error","bug","session"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}