Automattic/mongoose · error · TypeError

Invalid sort value: { ${key}: ${val} }

Error message

Invalid sort value: { ${key}: ${val} }

What it means

_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.

Source

Thrown at lib/query.js:3209

  return this;
};

/*!
 * Convert sort values
 */

function _handleSortValue(val, key) {
  if (val === 1 || val === 'asc' || val === 'ascending') {
    return 1;
  }
  if (val === -1 || val === 'desc' || val === 'descending') {
    return -1;
  }
  if (val?.$meta != null) {
    return { $meta: val.$meta };
  }
  throw new TypeError('Invalid sort value: { ' + key + ': ' + val + ' }');
}

/**
 * Declare and/or execute this query as a `deleteOne()` operation. Works like
 * remove, except it deletes at most one document regardless of the `single`
 * option.
 *
 * This function triggers `deleteOne` middleware.
 *
 * #### Example:
 *
 *     await Character.deleteOne({ name: 'Eddard Stark' });
 *
 * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteOne).
 * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
 * object that contains 2 properties:
 *
 * - `acknowledged`: boolean

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Normalize input before use: trim + lowercase, then map 'asc'/'ascending'/'1' to 1 and 'desc'/'descending'/'-1' to -1
  2. Whitelist field names and directions at the route boundary and reject invalid values with a 400
  3. Default an invalid direction to a safe value (e.g. 1) instead of forwarding it

Example fix

// before
const q = Model.find().sort({ name: req.query.order }); // req.query.order = 'ASC'

// after
const raw = String(req.query.order ?? '').trim().toLowerCase();
const dir = ['desc', 'descending', '-1'].includes(raw) ? -1 : 1;
const q = Model.find().sort({ name: dir });
Defensive patterns

Strategy: validation

Validate before calling

const DIRECTIONS = new Set(['asc', 'ascending', '1', 'desc', 'descending', '-1']);
function toDirection(raw) {
  const d = String(raw ?? '').trim().toLowerCase();
  if (!DIRECTIONS.has(d)) return 1;
  return d.startsWith('a') || d === '1' ? 1 : -1;
}
const q = Model.find().sort({ name: toDirection(req.query.order) });

Type guard

const isValidDirection = (v) => [1, -1, 'asc', 'ascending', 'desc', 'descending'].includes(v) || v?.$meta != null;

Prevention

When it happens

Trigger: .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 } }).

Common situations: User-supplied sort directions (req.query.order='ASC') forwarded unnormalized; uppercase 'ASC'/'DESC' from SQL-style APIs; truthy numbers mistaken for directions.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/1b09dee64007b5f6. Report an issue: GitHub.