Automattic/mongoose · error · CastError

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

Error message

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

What it means

SchemaNumber.cast wraps the number caster (lib/cast/number.js) and rethrows failures as CastError 'Number'. Valid input: null/undefined, empty string (→ null), numeric strings ('42'), booleans (true → 1), Number objects, values with numeric valueOf(), and arrays are rejected outright. Strings like 'abc' or '12px' and objects without numeric coercion throw 'Cast to Number failed: value is not a valid number', wrapped into this CastError.

Source

Thrown at lib/schema/number.js:412

  }

  const val = value?._id !== undefined ?
    value._id : // documents
    value;

  let castNumber;
  if (typeof this._castFunction === 'function') {
    castNumber = this._castFunction;
  } else if (typeof this.constructor.cast === 'function') {
    castNumber = this.constructor.cast();
  } else {
    castNumber = SchemaNumber.cast();
  }

  try {
    return castNumber(val);
  } catch (err) {
    throw new CastError('Number', val, this.path, err, this);
  }
};

/*!
 * ignore
 */

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

function handleArray(val) {
  const _this = this;
  if (!Array.isArray(val)) {
    return [this.cast(val)];
  }
  return val.map(function(m) {
    return _this.cast(m);

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Coerce explicitly at the boundary: `const n = Number(v); if (Number.isNaN(n)) reject;` then assign
  2. Strip units/currency first when the format is known: `Number(String(v).replace(/[^0-9.-]/g, ''))`
  3. For decimals-as-strings with locale commas, normalize separators before casting
  4. Register a lenient custom caster only if you accept the loss of strictness: `mongoose.Schema.Types.Number.cast(v => ...)`

Example fix

// before
product.price = req.body.price; // '12.99 USD'

// after
const price = Number(String(req.body.price).replace(/[^0-9.-]/g, ''));
if (Number.isNaN(price)) throw new ValidationError('bad price');
product.price = price;
Defensive patterns

Strategy: validation

Validate before calling

function toNumber(v) {
  if (v == null || v === '') return null;
  if (Array.isArray(v)) throw new Error('array is not a number');
  const n = typeof v === 'string' || typeof v === 'boolean' ? Number(v) : v;
  if (typeof n !== 'number' || Number.isNaN(n)) throw new Error(`not a number: ${v}`);
  return n;
}
product.price = toNumber(req.body.price);

Type guard

function isNumberLike(v) {
  if (v == null || v === '' || typeof v === 'number' || typeof v === 'boolean') return true;
  if (typeof v === 'string') return !Number.isNaN(Number(v));
  return !Array.isArray(v) && !Number.isNaN(Number(v?.valueOf?.()));
}

Try / catch

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

Prevention

When it happens

Trigger: `doc.age = 'abc'`, `doc.age = '12px'`, `doc.age = '$10'`, `doc.age = {}`, `doc.age = [5]` (arrays rejected by the caster's guards), or queries like `{ age: { $gt: 'unknown' } }`.

Common situations: HTML form fields always arrive as strings and skip coercion; unit-suffixed input from IoT/CSV feeds ('10px', '5kg'); currency strings; JSON APIs returning numbers as strings; parseInt/parseFloat never applied before assignment.

Related errors


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