{"record":{"id":"1b09dee64007b5f6","repo":"Automattic/mongoose","slug":"invalid-sort-value-key-val","errorCode":null,"errorMessage":"Invalid sort value: { ${key}: ${val} }","messagePattern":"Invalid sort value: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":3209,"sourceCode":"\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 };\n  }\n  throw new TypeError('Invalid sort value: { ' + key + ': ' + val + ' }');\n}\n\n/**\n * Declare and/or execute this query as a `deleteOne()` operation. Works like\n * remove, except it deletes at most one document regardless of the `single`\n * option.\n *\n * This function triggers `deleteOne` middleware.\n *\n * #### Example:\n *\n *     await Character.deleteOne({ name: 'Eddard Stark' });\n *\n * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteOne).\n * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an\n * object that contains 2 properties:\n *\n * - `acknowledged`: boolean","sourceCodeStart":3191,"sourceCodeEnd":3227,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L3191-L3227","documentation":"_handleSortValue(), used by sort()'s object, array, and Map branches, accepts exactly 1, -1, 'asc', 'ascending', 'desc', 'descending', or a { $meta: ... } object; anything else throws a TypeError echoing the offending key/value. Comparisons are exact, so 'ASC' (uppercase), 'desc ' (trailing space), 0, 2, and 'up' all fail.","triggerScenarios":".sort({ name: 'ASC' }) with uppercase from SQL habits; .sort({ age: 2 }) or .sort({ age: 0 }); .sort({ name: 'up' }); passing req.query.order straight into the sort object; .sort({ score: { $meta: undefined } }).","commonSituations":"User-supplied sort directions (req.query.order='ASC') forwarded unnormalized; uppercase 'ASC'/'DESC' from SQL-style APIs; truthy numbers mistaken for directions.","solutions":["Normalize input before use: trim + lowercase, then map 'asc'/'ascending'/'1' to 1 and 'desc'/'descending'/'-1' to -1","Whitelist field names and directions at the route boundary and reject invalid values with a 400","Default an invalid direction to a safe value (e.g. 1) instead of forwarding it"],"exampleFix":"// before\nconst q = Model.find().sort({ name: req.query.order }); // req.query.order = 'ASC'\n\n// after\nconst raw = String(req.query.order ?? '').trim().toLowerCase();\nconst dir = ['desc', 'descending', '-1'].includes(raw) ? -1 : 1;\nconst q = Model.find().sort({ name: dir });","handlingStrategy":"validation","validationCode":"const DIRECTIONS = new Set(['asc', 'ascending', '1', 'desc', 'descending', '-1']);\nfunction toDirection(raw) {\n  const d = String(raw ?? '').trim().toLowerCase();\n  if (!DIRECTIONS.has(d)) return 1;\n  return d.startsWith('a') || d === '1' ? 1 : -1;\n}\nconst q = Model.find().sort({ name: toDirection(req.query.order) });","typeGuard":"const isValidDirection = (v) => [1, -1, 'asc', 'ascending', 'desc', 'descending'].includes(v) || v?.$meta != null;","tryCatchPattern":null,"preventionTips":["Trim and lowercase user-supplied directions before use","Whitelist sort fields and directions at the API boundary","Remember 'ASC'/'DESC' uppercase values are rejected — exact match only"],"tags":["mongoose","query","sort","typeerror","invalid-value","request-params"],"backgroundTag":"invalid-sort-value","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}