{"id":"b02f5e749d2c6261","repo":"mongodb/node-mongodb-native","slug":"unexpected-null-session-a-cursor-creating-command-b02f5e","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":"error","filePath":"src/cursor/run_command_cursor.ts","lineNumber":164,"sourceCode":"    const operation = new RunCursorCommandOperation(this.db.s.namespace, this.command, {\n      ...this.cursorOptions,\n      session: session,\n      readPreference: this.cursorOptions.readPreference\n    });\n\n    const response = await executeOperation(this.client, operation, this.timeoutContext);\n\n    return {\n      server: operation.server,\n      session,\n      response\n    };\n  }\n\n  /** @internal */\n  override async getMore(): Promise<CursorResponse> {\n    if (!this.session) {\n      throw new MongoRuntimeError(\n        'Unexpected null session. A cursor creating command should have set this'\n      );\n    }\n\n    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n    const getMoreOperation = new GetMoreOperation(this.namespace, this.id!, this.server!, {\n      ...this.cursorOptions,\n      session: this.session,\n      ...this.getMoreOptions\n    });\n\n    return await executeOperation(this.client, getMoreOperation, this.timeoutContext);\n  }\n}\n","sourceCodeStart":146,"sourceCodeEnd":179,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cursor/run_command_cursor.ts#L146-L179","documentation":"A defensive invariant in RunCommandCursor.getMore() that asserts a ClientSession exists. A session is allocated during cursor initialization (the first _read/next), so reaching getMore without one indicates the cursor lifecycle was violated (reused after close, manipulated out of order) or a driver-internal bug. It is a MongoRuntimeError, not user input.","triggerScenarios":"Calling getMore after the cursor was closed/killed, reusing a cursor after it errored or completed, concurrent iteration of the same cursor, or a driver regression.","commonSituations":"Race conditions; manual cursor cleanup; re-entering an async iterator after it threw; driver version mismatches.","solutions":["Do not reuse a RunCommandCursor after it has been closed, completed, or errored","Create a fresh RunCommandCursor for each independent iteration","Avoid concurrent iteration of a single cursor instance","If reproducible on the latest driver version, file a bug with the command and exact lifecycle"],"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/i.test(err.message)) {\n    // cursor lifecycle is broken; discard and recreate\n    cursor = db.runCursorCommand(cmd);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Never reuse a cursor after it completes or errors","Avoid concurrent iteration of the same cursor instance","Allocate a fresh RunCommandCursor for each independent pass"],"tags":["cursor","internal","session","runcommandcursor"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}