{"record":{"id":"0bcaf239cb3fc7ea","repo":"mongodb/node-mongodb-native","slug":"name-is-not-a-valid-query-modifier","errorCode":null,"errorMessage":"${name} is not a valid query modifier","messagePattern":"(.+?) is not a valid query modifier","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/cursor/find_cursor.ts","lineNumber":268,"sourceCode":"   *\n   * @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.\n   */\n  showRecordId(value: boolean): this {\n    this.throwIfInitialized();\n    this.findOptions.showRecordId = value;\n    return this;\n  }\n\n  /**\n   * Add a query modifier to the cursor query\n   *\n   * @param name - The query modifier (must start with $, such as $orderby etc)\n   * @param value - The modifier value.\n   */\n  addQueryModifier(name: string, value: string | boolean | number | Document): this {\n    this.throwIfInitialized();\n    if (name[0] !== '$') {\n      throw new MongoInvalidArgumentError(`${name} is not a valid query modifier`);\n    }\n\n    // Strip of the $\n    const field = name.substr(1);\n\n    // NOTE: consider some TS magic for this\n    switch (field) {\n      case 'comment':\n        this.findOptions.comment = value;\n        break;\n\n      case 'explain':\n        this.findOptions.explain = value as boolean;\n        break;\n\n      case 'hint':\n        this.findOptions.hint = value as string | Document;\n        break;","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cursor/find_cursor.ts#L250-L286","documentation":"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.","triggerScenarios":"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 '$'.","commonSituations":"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.","solutions":["Prefix the modifier name with '$': addQueryModifier('$orderby', value).","Prefer the typed API instead: cursor.sort(), cursor.hint(), cursor.maxTimeMS() rather than addQueryModifier.","If the name is dynamic, ensure it is normalized with `name.startsWith('$') ? name : '$' + name`."],"exampleFix":"// before\ncursor.addQueryModifier('orderby', { age: 1 });\n\n// after\ncursor.sort({ age: 1 });\n// or\ncursor.addQueryModifier('$orderby', { age: 1 });","handlingStrategy":"validation","validationCode":"function addModifier(cursor, name, value) {\n  if (!name.startsWith('$')) name = '$' + name;\n  return cursor.addQueryModifier(name, value);\n}","typeGuard":"function isValidModifierName(name) { return typeof name === 'string' && name.startsWith('$'); }","tryCatchPattern":null,"preventionTips":["Prefer typed methods (cursor.sort, cursor.hint, cursor.comment) over addQueryModifier.","Normalize dynamic modifier names with a leading '$' before calling addQueryModifier."],"tags":["find","query-modifier","options-validation"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}