{"record":{"id":"a05df9d4dda7189b","repo":"Automattic/mongoose","slug":"invalid-sort-argument-must-be-a-string-object","errorCode":null,"errorMessage":"Invalid sort() argument. Must be a string, object, array, or map.","messagePattern":"Invalid sort\\(\\) argument\\. Must be a string, object, array, or map\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":3189,"sourceCode":"      sort[key] = _handleSortValue(pair[1], key);\n    }\n  } else if (typeof arg === 'object' && arg != null && !(arg instanceof Map)) {\n    for (const key of Object.keys(arg)) {\n      if (specialProperties.has(key)) {\n        continue;\n      }\n      sort[key] = _handleSortValue(arg[key], key);\n    }\n  } else if (arg instanceof Map) {\n    for (let key of arg.keys()) {\n      key = '' + key;\n      if (specialProperties.has(key)) {\n        continue;\n      }\n      sort[key] = _handleSortValue(arg.get(key), key);\n    }\n  } else if (arg != null) {\n    throw new TypeError('Invalid sort() argument. Must be a string, object, array, or map.');\n  }\n\n  return this;\n};\n\n/*!\n * Convert sort values\n */\n\nfunction _handleSortValue(val, key) {\n  if (val === 1 || val === 'asc' || val === 'ascending') {\n    return 1;\n  }\n  if (val === -1 || val === 'desc' || val === 'descending') {\n    return -1;\n  }\n  if (val?.$meta != null) {\n    return { $meta: val.$meta };","sourceCodeStart":3171,"sourceCodeEnd":3207,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L3171-L3207","documentation":"Terminal guard in sort(): after the string, array, plain object, and Map branches, any remaining non-null value throws. Numbers, booleans, symbols, and functions have no branch, so the most common hit is .sort(-1), which developers read as 'sort descending' but which is just an invalid specification.","triggerScenarios":".sort(-1) or .sort(1) meaning direction; .sort(true); .sort(() => 'a'); a dynamically computed sort variable that turns out to be a number or boolean.","commonSituations":"Passing -1 as the whole sort expecting descending order; booleans from feature flags; untyped config values flowing into the query builder.","solutions":["For descending use .sort({ field: -1 }) or .sort('-field')","Validate dynamic sort specs and default to a valid spec or omit sort() entirely","Type sort variables as string | Record<string, 1 | -1> | Array<[string, 1 | -1]>"],"exampleFix":"// before\nModel.find().sort(-1); // invalid: -1 is not a sort spec\n\n// after\nModel.find().sort({ createdAt: -1 });\n// or\nModel.find().sort('-createdAt');","handlingStrategy":"type-guard","validationCode":"const ALLOWED_SORTS = { newest: { createdAt: -1 }, oldest: { createdAt: 1 } };\nconst spec = ALLOWED_SORTS[String(req.query.sort ?? 'newest')] ?? { createdAt: -1 };\nconst q = Model.find().sort(spec);","typeGuard":"function isSortSpec(v) {\n  return v == null || typeof v === 'string' || Array.isArray(v) || v instanceof Map ||\n    (typeof v === 'object' && v !== null);\n}","tryCatchPattern":null,"preventionTips":["Map allowed user sort keys to predefined specs instead of forwarding raw values","Never call .sort(-1); direction lives inside the spec","Type sort variables narrowly (string | object | array of pairs | Map)"],"tags":["mongoose","query","sort","typeerror","invalid-argument"],"backgroundTag":"invalid-sort-argument","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}