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
Mongoose throws this CastError when an update applies a numeric-only update operator ($inc or $pop) to a path that is not defined in the schema and the value cannot be converted to a number. Even for schema-less paths (schema is null in castUpdateVal), Mongoose still runs castNumber for these operators so that garbage never reaches MongoDB, and wraps the failure in a CastError with the raw value, its JS type, and the offending path.
Source
Thrown at lib/helpers/query/castUpdate.js:571
* Casts `val` according to `schema` and atomic `op`.
*
* @param {SchemaType} schema
* @param {object} val
* @param {string} op the atomic operator ($pull, $set, etc)
* @param {string} $conditional
* @param {Query} context
* @param {string} path
* @api private
*/
function castUpdateVal(schema, val, op, $conditional, context, path) {
if (!schema) {
// non-existing schema path
if (op in numberOps) {
try {
return castNumber(val);
} catch {
throw new CastError('number', val, path);
}
}
return val;
}
const cond = schema.$isMongooseArray
&& op in castOps
&& (utils.isObject(val) || Array.isArray(val));
if (cond && !overwriteOps[op]) {
// Cast values for ops that add data to MongoDB.
// Ensures embedded documents get ObjectIds etc.
let schemaArrayDepth = 0;
let cur = schema;
while (cur.$isMongooseArray) {
++schemaArrayDepth;
cur = cur.embeddedSchemaType;
}
let arrayDepth = 0;View on GitHub (pinned to 49cdab0136)
Solutions
- Pass a number (or numeric string) as the $inc/$pop value, e.g. { $inc: { [path]: Number(value) } }, and drop values that coerce to NaN
- If the field is legitimate, define it in the schema (e.g. new Schema({ count: Number, ... }, { strict: false }))
- Fix the typo so the path matches an existing schema path
- Whitelist update paths and operators before handing user input to update operations
Example fix
// before
await Model.updateOne({ _id }, { $inc: { scroes: req.body.amount } }); // typo, not in schema
// after
await Model.updateOne({ _id }, { $inc: { scores: Number(req.body.amount) } }); Defensive patterns
Strategy: validation
Validate before calling
// Before updateOne with $inc/$pop from untrusted values
function safeNumericUpdate(path, value) {
const n = Number(value);
if (value == null || !Number.isFinite(n)) {
throw new Error(`Invalid numeric operand for ${path}: ${JSON.stringify(value)}`);
}
return n;
}
await Model.updateOne({ _id }, { $inc: { [path]: safeNumericUpdate(path, req.body.value) } }); Type guard
const isNumericOperand = (v) => v != null && Number.isFinite(Number(v));
Try / catch
try {
await Model.updateOne(filter, update);
} catch (err) {
if (err instanceof mongoose.Error.CastError && err.kind === 'number') {
// log offending err.path / err.value and reject the request with 400
} else throw err;
} Prevention
- Whitelist the exact paths allowed in $inc/$pop updates instead of forwarding user objects
- Declare every incrementable field in the schema so casting happens against a SchemaType
- Coerce and validate inputs (Number.isFinite) before building update objects
When it happens
Trigger: Model.updateOne()/updateMany()/findOneAndUpdate() with { $inc: { notInSchema: 'abc' } } or { $pop: { typoPath: 'x' } } on a schema with strict: false (with default strict mode the unknown path is stripped before reaching this cast). Typical shape: dynamically building $inc objects from user input where a non-numeric string sneaks in.
Common situations: Schemas with strict:false that accept arbitrary fields; typos in field names inside $inc updates; REST APIs forwarding request-body values straight into update operators; converting string values (form data, env vars, query params) without coercion.
Related errors
- Cast to number failed for value "${value}" (type ${valueType
- a circular reference in the update value, updateValue: ${uti
- Invalid atomic update value for ${op}. Expected an object, r
- Invalid update pipeline operator: "${op}"
- Field `${pathToCheck}` is not in schema and strict mode is s
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/812698e19129c6e3.
Report an issue: GitHub.