{"id":"acc895fcdff9d4a5","repo":"mongodb/node-mongodb-native","slug":"cursor-is-already-initialized","errorCode":null,"errorMessage":"Cursor is already initialized","messagePattern":"Cursor is already initialized","errorType":"exception","errorClass":"MongoCursorInUseError","httpStatus":null,"severity":"error","filePath":"src/cursor/abstract_cursor.ts","lineNumber":1091,"sourceCode":"      if (transformedDocument === null) {\n        const TRANSFORM_TO_NULL_ERROR =\n          'Cursor returned a `null` document, but the cursor is not exhausted.  Mapping documents to `null` is not supported in the cursor transform.';\n        throw new MongoAPIError(TRANSFORM_TO_NULL_ERROR);\n      }\n      return transformedDocument;\n    } catch (transformError) {\n      try {\n        await this.close();\n      } catch (closeError) {\n        squashError(closeError);\n      }\n      throw transformError;\n    }\n  }\n\n  /** @internal */\n  protected throwIfInitialized() {\n    if (this.initialized) throw new MongoCursorInUseError();\n  }\n}\n\nclass ReadableCursorStream extends Readable {\n  private _cursor: AbstractCursor;\n  private _readInProgress = false;\n\n  constructor(cursor: AbstractCursor) {\n    super({\n      objectMode: true,\n      autoDestroy: false,\n      highWaterMark: 1\n    });\n    this._cursor = cursor;\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  override _read(size: number): void {","sourceCodeStart":1073,"sourceCodeEnd":1109,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cursor/abstract_cursor.ts#L1073-L1109","documentation":"Thrown as MongoCursorInUseError by AbstractCursor.throwIfInitialized() when a cursor-mutating builder method (filter, sort, limit, skip, addStage, project, map, etc.) is called after the cursor has already been initialized (i.e. iteration has begun). Once initialized=true, the cursor's command is effectively fixed and the driver forbids further structural changes. 'Cursor is already initialized' is the implicit message carried by MongoCursorInUseError.","triggerScenarios":"Calling coll.find(); then awaiting cursor.next(); then cursor.sort(...) or cursor.limit(...); also clone() is fine but chaining .limit().sort() after .next() throws. Reusing a cursor variable across two iterations also trips this.","commonSituations":"Building a query incrementally after a peek, reusing a cursor stored in a variable that was already partially consumed, or calling cursor.count()/explain() then mutating.","solutions":["Set all options before the first await on the cursor (chain .filter().sort().limit() first, then iterate)","If you need a changed query, call cursor.clone() to get a fresh un-initialized copy and modify that","Do not reuse a cursor variable after iteration has started; create a new find()/aggregate()"],"exampleFix":"// before\nconst c = coll.find({});\nawait c.next();\nc.sort({ x: 1 }); // throws MongoCursorInUseError\n// after\nconst c = coll.find({}).sort({ x: 1 });\nawait c.next();","handlingStrategy":"validation","validationCode":"function assertFresh(cursor) {\n  // No public flag; track iteration yourself\n  if (cursor.__started) throw new Error('cursor already initialized');\n}\n// Prefer: build the full query before iterating.","typeGuard":null,"tryCatchPattern":"try {\n  cursor.sort({ x: 1 });\n} catch (e) {\n  if (e instanceof MongoCursorInUseError) {\n    cursor = cursor.clone().sort({ x: 1 }); // start fresh\n  } else throw e;\n}","preventionTips":["Chain all builder methods before the first await/iteration","Call clone() to get a fresh, modifiable copy of a used cursor","Create a new find()/aggregate() rather than reusing a consumed cursor"],"tags":["cursor","lifecycle","builder","user-error"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}