mongodb/node-mongodb-native · error · MongoInvalidArgumentError

is not a valid query modifier

Error message

${name} is not a valid query modifier

What it means

Thrown by FindCursor.addQueryModifier when the name argument does not begin with '$'. Query modifiers are raw wire-protocol fields like $orderby, $hint, $maxTimeMS, so the driver requires the leading dollar sign. This is a sanity check before it strips the '$' to map the modifier to a typed findOptions field.

Solutions

  1. Prefix the modifier name with '$': addQueryModifier('$orderby', value).
  2. Prefer the typed API instead: cursor.sort(), cursor.hint(), cursor.maxTimeMS() rather than addQueryModifier.
  3. If the name is dynamic, ensure it is normalized with `name.startsWith('$') ? name : '$' + name`.

Example fix

// before
cursor.addQueryModifier('orderby', { age: 1 });

// after
cursor.sort({ age: 1 });
// or
cursor.addQueryModifier('$orderby', { age: 1 });
Defensive patterns

Strategy: validation

Validate before calling

function addModifier(cursor, name, value) {
  if (!name.startsWith('$')) name = '$' + name;
  return cursor.addQueryModifier(name, value);
}

Type guard

function isValidModifierName(name) { return typeof name === 'string' && name.startsWith('$'); }

Prevention

When it happens

Trigger: cursor.addQueryModifier('orderby', { age: 1 }) instead of '$orderby'; passing a field name from a user-supplied string that was not normalized; building a modifier name dynamically without prefixing '$'.

Common situations: Translating a human-readable sort hint into a query modifier; using addQueryModifier instead of the typed .sort()/.hint() methods; code that strips '$' upstream and forgets to re-add it.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/0bcaf239cb3fc7ea. Report an issue: GitHub.

Appendix: source

Thrown at src/cursor/find_cursor.ts:268

   *
   * @param value - The $showDiskLoc option has now been deprecated and replaced with the showRecordId field. $showDiskLoc will still be accepted for OP_QUERY stye find.
   */
  showRecordId(value: boolean): this {
    this.throwIfInitialized();
    this.findOptions.showRecordId = value;
    return this;
  }

  /**
   * Add a query modifier to the cursor query
   *
   * @param name - The query modifier (must start with $, such as $orderby etc)
   * @param value - The modifier value.
   */
  addQueryModifier(name: string, value: string | boolean | number | Document): this {
    this.throwIfInitialized();
    if (name[0] !== '$') {
      throw new MongoInvalidArgumentError(`${name} is not a valid query modifier`);
    }

    // Strip of the $
    const field = name.substr(1);

    // NOTE: consider some TS magic for this
    switch (field) {
      case 'comment':
        this.findOptions.comment = value;
        break;

      case 'explain':
        this.findOptions.explain = value as boolean;
        break;

      case 'hint':
        this.findOptions.hint = value as string | Document;
        break;

View on GitHub (pinned to dce7939f86)