mongodb/node-mongodb-native · error · MongoRuntimeError

Unexpected null selectedServer. A cursor creating command sh

Error message

Unexpected null selectedServer. A cursor creating command should have set this

What it means

Thrown by AbstractCursor.getMore() when selectedServer is null while dispatching a getMore. selectedServer is populated during cursorInit() from the _initialize() result (abstract_cursor.ts:915) and is required to route the getMore to the correct server. Like the null cursorId check, this should be unreachable through public iteration APIs because cursorInit() runs before getMore() in fetchBatch().

Source

Thrown at src/cursor/abstract_cursor.ts:866

  /**
   * Returns a new uninitialized copy of this cursor, with options matching those that have been set on the current instance
   */
  abstract clone(): AbstractCursor<TSchema>;

  /** @internal */
  protected abstract _initialize(
    session: ClientSession | undefined
  ): Promise<InitialCursorResponse>;

  /** @internal */
  async getMore(): Promise<CursorResponse> {
    if (this.cursorId == null) {
      throw new MongoRuntimeError(
        'Unexpected null cursor id. A cursor creating command should have set this'
      );
    }
    if (this.selectedServer == null) {
      throw new MongoRuntimeError(
        'Unexpected null selectedServer. A cursor creating command should have set this'
      );
    }

    if (this.cursorSession == null) {
      throw new MongoRuntimeError(
        'Unexpected null session. A cursor creating command should have set this'
      );
    }
    const getMoreOptions = {
      ...this.cursorOptions,
      session: this.cursorSession,
      batchSize: this.cursorOptions.batchSize
    };

    const getMoreOperation = new GetMoreOperation(
      this.cursorNamespace,
      this.cursorId,

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Ensure any custom _initialize() returns a real server object (e.g. AggregateOperation/FindOperation .server after executeOperation)
  2. Do not invoke the internal getMore() directly; iterate via next()/for-await/toArray()
  3. Verify you are not reusing a cursor instance whose server reference was cleared by cleanup()

Example fix

// before
async _initialize(session) {
  const resp = await executeOperation(this.client, op);
  return { response: resp, session }; // missing server
}
// after
async _initialize(session) {
  const resp = await executeOperation(this.client, op);
  return { response: resp, server: op.server, session };
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate custom _initialize output before returning it
async _initialize(session) {
  const response = await executeOperation(this.client, op);
  if (!op.server) throw new Error('no server selected');
  return { response, server: op.server, session };
}

Prevention

When it happens

Trigger: A custom _initialize() implementation that returns an InitialCursorResponse without a server field, or direct invocation of the @internal getMore() before the cursor has been initialized. Also theoretically possible if server selection was somehow stripped after init.

Common situations: Custom cursor subclasses, test harnesses that stub _initialize() incompletely, or integration with mock libraries that replace the init response object.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/9cab7e9773bf0ebd.json. Report an issue: GitHub.