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
- Remove the cursor option entirely (the driver sets a default cursor automatically).
- If you need batchSize, pass it as cursor: { batchSize: N } or directly as batchSize: N.
- 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
- Don't pass cursor:true (driver sets a default cursor)
- Pass batchSize directly or as cursor:{batchSize:N}
- Sanitize user-supplied option objects before aggregate
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
- Flag ${flag} must be a boolean value
- Invalid read preference: ${readPreference}
- Argument for maxTimeMS must be a number
- Operation "batchSize" requires an integer
- User input for option 'mongodbLogComponentSeverities' object
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/fa114b5f86ed9744.json.
Report an issue: GitHub.