Automattic/mongoose · error · CastError

Cast to Double failed for value "${value}" (type ${valueType

Error message

Cast to Double failed for value "${value}" (type ${valueType}) at path "${path}"

What it means

SchemaDouble.cast converts failures of the Double caster (lib/cast/double.js) into this CastError. Valid input: null/undefined and empty string (→ null), numbers, BSON Long, strings parsed by BSON's strict `Double.fromString`, and objects whose valueOf()/toString() yields a parseable decimal string. Arrays, plain objects without numeric coercion, and non-decimal strings ('abc') throw.

Source

Thrown at lib/schema/double.js:189

 * @param {object} value
 * @param {object} model this value is optional
 * @api private
 */

SchemaDouble.prototype.cast = function(value) {
  let castDouble;
  if (typeof this._castFunction === 'function') {
    castDouble = this._castFunction;
  } else if (typeof this.constructor.cast === 'function') {
    castDouble = this.constructor.cast();
  } else {
    castDouble = SchemaDouble.cast();
  }

  try {
    return castDouble(value);
  } catch (error) {
    throw new CastError('Double', value, this.path, error, this);
  }
};

/*!
 * ignore
 */

function handleSingle(val) {
  return this.cast(val);
}

const $conditionalHandlers = {
  ...SchemaType.prototype.$conditionalHandlers,
  $gt: handleSingle,
  $gte: handleSingle,
  $lt: handleSingle,
  $lte: handleSingle
};

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Assign plain numbers: `doc.ratio = 1.5`
  2. Strip/validate strings to decimal format before assigning (regex `/^[+-]?\d+(\.\d+)?$/`)
  3. Coerce with `Number(v)` at the boundary and check `Number.isNaN`
  4. For arrays of readings, map to numbers explicitly instead of assigning the array

Example fix

// before
metric.ratio = req.body.ratio; // '1,5' or 'n/a'

// after
const ratio = Number(String(req.body.ratio).replace(',', '.'));
if (Number.isNaN(ratio)) throw new ValidationError('bad ratio');
metric.ratio = ratio;
Defensive patterns

Strategy: validation

Validate before calling

function toDouble(v) {
  if (v == null || v === '') return null;
  if (Array.isArray(v)) throw new Error('array is not a double');
  const n = typeof v === 'number' ? v : Number(String(v).replace(',', '.'));
  if (Number.isNaN(n)) throw new Error(`not a double: ${v}`);
  return n;
}
metric.ratio = toDouble(req.body.ratio);

Type guard

function isDoubleLike(v) {
  if (v == null || v === '' || typeof v === 'number') return true;
  return typeof v === 'string' && !Number.isNaN(Number(v.replace(',', '.')));
}

Try / catch

try { doc.ratio = v; } catch (err) { if (err.name === 'CastError' && err.kind === 'Double') { return badRequest(`${err.path} must be a number`); } throw err; }

Prevention

When it happens

Trigger: `doc.ratio = 'abc'`, `doc.ratio = [1.5]` (arrays explicitly rejected), `doc.ratio = {}`, or an object whose `valueOf()` returns a non-parseable string. Query side: `{ ratio: { $gt: 'n/a' } }`.

Common situations: Mongoose 8's Double type used with un-sanitized string input from forms or spreadsheets; locale-formatted numbers ('1,5'); expecting JS-style loose Number coercion ('5px' works with Number() but Double.fromString rejects it); passing arrays from destructuring mistakes.

Related errors


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