{"record":{"id":"4fb8c3d1b8e03033","repo":"Automattic/mongoose","slug":"cast-to-number-failed-for-value-value-type-4fb8c3","errorCode":null,"errorMessage":"Cast to Number failed for value \"${value}\" (type ${valueType}) at path \"${path}\"","messagePattern":"Cast to Number failed for value \"(.+?)\" \\(type (.+?)\\) at path \"(.+?)\"","errorType":"validation","errorClass":"CastError","httpStatus":null,"severity":"error","filePath":"lib/schema/number.js","lineNumber":412,"sourceCode":"  }\n\n  const val = value?._id !== undefined ?\n    value._id : // documents\n    value;\n\n  let castNumber;\n  if (typeof this._castFunction === 'function') {\n    castNumber = this._castFunction;\n  } else if (typeof this.constructor.cast === 'function') {\n    castNumber = this.constructor.cast();\n  } else {\n    castNumber = SchemaNumber.cast();\n  }\n\n  try {\n    return castNumber(val);\n  } catch (err) {\n    throw new CastError('Number', val, this.path, err, this);\n  }\n};\n\n/*!\n * ignore\n */\n\nfunction handleSingle(val) {\n  return this.cast(val);\n}\n\nfunction handleArray(val) {\n  const _this = this;\n  if (!Array.isArray(val)) {\n    return [this.cast(val)];\n  }\n  return val.map(function(m) {\n    return _this.cast(m);","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/schema/number.js#L394-L430","documentation":"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.","triggerScenarios":"`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' } }`.","commonSituations":"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.","solutions":["Coerce explicitly at the boundary: `const n = Number(v); if (Number.isNaN(n)) reject;` then assign","Strip units/currency first when the format is known: `Number(String(v).replace(/[^0-9.-]/g, ''))`","For decimals-as-strings with locale commas, normalize separators before casting","Register a lenient custom caster only if you accept the loss of strictness: `mongoose.Schema.Types.Number.cast(v => ...)`"],"exampleFix":"// before\nproduct.price = req.body.price; // '12.99 USD'\n\n// after\nconst price = Number(String(req.body.price).replace(/[^0-9.-]/g, ''));\nif (Number.isNaN(price)) throw new ValidationError('bad price');\nproduct.price = price;","handlingStrategy":"validation","validationCode":"function toNumber(v) {\n  if (v == null || v === '') return null;\n  if (Array.isArray(v)) throw new Error('array is not a number');\n  const n = typeof v === 'string' || typeof v === 'boolean' ? Number(v) : v;\n  if (typeof n !== 'number' || Number.isNaN(n)) throw new Error(`not a number: ${v}`);\n  return n;\n}\nproduct.price = toNumber(req.body.price);","typeGuard":"function isNumberLike(v) {\n  if (v == null || v === '' || typeof v === 'number' || typeof v === 'boolean') return true;\n  if (typeof v === 'string') return !Number.isNaN(Number(v));\n  return !Array.isArray(v) && !Number.isNaN(Number(v?.valueOf?.()));\n}","tryCatchPattern":"try { doc.qty = v; } catch (err) { if (err.name === 'CastError' && err.kind === 'Number') { return badRequest(`${err.path} must be a number`); } throw err; }","preventionTips":["HTML forms send strings — coerce in the request handler","Strip units/currency before casting","Validate with zod/joi `.coerce.number()` at the boundary"],"tags":["mongoose","number","cast","validation","coercion"],"backgroundTag":"mongoose-cast-error","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}