mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Cursor options must be an object

Error message

Cursor options must be an object

What it means

Thrown by the AggregateOperation constructor when options.cursor is defined but is not an object. In the aggregation command, cursor must be a document (e.g. { batchSize: 100 }); passing a boolean or number triggers MongoInvalidArgumentError because the wire protocol expects an object for the cursor field.

Source

Thrown at src/operations/aggregate.ts:84

    // determine if we have a write stage, override read preference if so
    this.hasWriteStage = false;
    if (typeof options?.out === 'string') {
      this.pipeline = this.pipeline.concat({ $out: options.out });
      this.hasWriteStage = true;
    } else if (pipeline.length > 0) {
      const finalStage = pipeline[pipeline.length - 1];
      if (finalStage.$out || finalStage.$merge) {
        this.hasWriteStage = true;
      }
    }

    if (!this.hasWriteStage) {
      delete this.options.writeConcern;
    }

    if (options?.cursor != null && typeof options.cursor !== 'object') {
      throw new MongoInvalidArgumentError('Cursor options must be an object');
    }

    this.SERVER_COMMAND_RESPONSE_TYPE = this.explain ? ExplainedCursorResponse : CursorResponse;
  }

  override get commandName() {
    return 'aggregate' as const;
  }

  override get canRetryRead(): boolean {
    return !this.hasWriteStage;
  }

  addToPipeline(stage: Document): void {
    this.pipeline.push(stage);
  }

  override buildCommandDocument(): Document {

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Remove the cursor option entirely (the driver sets a default cursor automatically).
  2. If you need batchSize, pass it as cursor: { batchSize: N } or directly as batchSize: N.
  3. Validate user-supplied options: delete opts.cursor if it is not an object before calling aggregate.

Example fix

// before
collection.aggregate(pipeline, { cursor: true });

// after
collection.aggregate(pipeline, { cursor: { batchSize: 100 } });
Defensive patterns

Strategy: validation

Validate before calling

if (opts.cursor != null && typeof opts.cursor !== 'object') delete opts.cursor;

Type guard

function isCursorObject(v): boolean { return v == null || (typeof v === 'object' && !Array.isArray(v)); }

Prevention

When it happens

Trigger: Calling collection.aggregate(pipeline, { cursor: true }) or { cursor: 1 } (legacy/loose-typed patterns). Spreading a config object whose cursor field is a non-object. Migrating from an older driver or shell habit.

Common situations: Old tutorials using cursor:true; dynamic options objects built from user input; copy-paste from mongo shell syntax.

Related errors


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