{"id":"ef37f36d28756c81","repo":"mongodb/node-mongodb-native","slug":"unexpected-null-cursor-id-a-cursor-creating-comma","errorCode":null,"errorMessage":"Unexpected null cursor id. A cursor creating command should have set this","messagePattern":"Unexpected null cursor id\\. A cursor creating command should have set this","errorType":"exception","errorClass":"MongoRuntimeError","httpStatus":null,"severity":"error","filePath":"src/cursor/abstract_cursor.ts","lineNumber":861,"sourceCode":"\n      this.cursorSession = null;\n    }\n  }\n\n  /**\n   * Returns a new uninitialized copy of this cursor, with options matching those that have been set on the current instance\n   */\n  abstract clone(): AbstractCursor<TSchema>;\n\n  /** @internal */\n  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","sourceCodeStart":843,"sourceCodeEnd":879,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cursor/abstract_cursor.ts#L843-L879","documentation":"Thrown by AbstractCursor.getMore() when the cursor's cursorId is null at the moment a getMore command is about to be dispatched. The cursorId is assigned during cursorInit() from the initial command response (abstract_cursor.ts:916); getMore() is only reached after fetchBatch() guards on cursorId == null and calls cursorInit() first. Seeing this error means the driver reached a state where initialization was skipped or failed to populate the id, which indicates an internal driver bug or a corrupted/monkey-patched cursor lifecycle.","triggerScenarios":"Reached only if getMore() is called directly while cursorId is still null, bypassing the fetchBatch() guard at abstract_cursor.ts:948. Realistic triggers: a subclass overriding _initialize() to return a response with a null/undefined id, calling the internal getMore() via test helpers, or a partially-applied monkey patch on cursorInit that swallows the assignment.","commonSituations":"Custom AbstractCursor subclasses, test code reaching into @internal methods, or a driver downgrade/upgrade mismatch where a cursor subclass from an older version is used with a newer runtime.","solutions":["If you subclass AbstractCursor, ensure _initialize() resolves an InitialCursorResponse whose response.id is a valid Long/number and never null","Stop calling the @internal getMore() method directly; use the public next()/toArray()/forEach() iteration APIs which call cursorInit() first","If you believe you hit this via normal API use, report a driver bug with a reproduction against the current mongodb package version"],"exampleFix":"// before (broken subclass)\nclass MyCursor extends AbstractCursor {\n  async _initialize() {\n    return { response: someResp, server, session }; // someResp.id is undefined\n  }\n}\n// after\nasync _initialize() {\n  const response = await executeOperation(this.client, op);\n  if (response.id == null) throw new Error('init returned no cursor id');\n  return { response, server: op.server, session };\n}","handlingStrategy":"validation","validationCode":"// Not user-reachable via public APIs; validate your subclass instead.\n// If you subclass AbstractCursor, assert the init response:\nasync function safeInit(cursor) {\n  const state = await cursor._initialize(undefined);\n  if (state.response.id == null) throw new Error('init returned null cursor id');\n  return state;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not call the @internal getMore() method directly","If subclassing AbstractCursor, always return an InitialCursorResponse with a non-null response.id","Report untriggered occurrences as driver bugs with a full reproduction"],"tags":["cursor","internal","getmore","driver-bug"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}