{"record":{"id":"3c962f55106a23b2","repo":"Automattic/mongoose","slug":"cast-to-number-failed-for-value-value-type-3c962f","errorCode":null,"errorMessage":"Cast to number failed for value \"${value}\" (type ${valueType}) at path \"${schema.path}\"","messagePattern":"Cast to number failed for value \"(.+?)\" \\(type (.+?)\\) at path \"(.+?)\"","errorType":"exception","errorClass":"CastError","httpStatus":null,"severity":"error","filePath":"lib/helpers/query/castUpdate.js","lineNumber":616,"sourceCode":"      val = [val];\n      ++arrayDepth;\n    }\n\n    let tmp = schema.applySetters(Array.isArray(val) ? val : [val], context);\n\n    for (let i = 0; i < additionalNesting; ++i) {\n      tmp = tmp[0];\n    }\n    return tmp;\n  }\n\n  if (op in noCastOps) {\n    return val;\n  }\n  if (op in numberOps) {\n    // Null and undefined not allowed for $pop, $inc\n    if (val == null) {\n      throw new CastError('number', val, schema.path);\n    }\n    if (op === '$inc') {\n      // Support `$inc` with long, int32, etc. (gh-4283)\n      return schema.castForQuery(\n        null,\n        val,\n        context\n      );\n    }\n    try {\n      return castNumber(val);\n    } catch {\n      throw new CastError('number', val, schema.path);\n    }\n  }\n  if (op === '$currentDate') {\n    if (typeof val === 'object') {\n      return { $type: val.$type };","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/helpers/query/castUpdate.js#L598-L634","documentation":"Mongoose rejects null and undefined as operands for the numeric update operators $inc and $pop before any casting is attempted (val == null check in castUpdateVal). These operators need an actual number to add or a 1/-1 pop direction, so a missing value is always a client-side bug. The CastError names the schema path being updated.","triggerScenarios":"Model.updateOne({}, { $inc: { count: null } }), { $pop: { items: undefined } }, or update objects built dynamically where a key survives with a null/undefined value (e.g. Object.fromEntries over req.body with explicit nulls, or pick() keeping empty form fields).","commonSituations":"Optional numeric form fields submitted empty and serialized as null; JSON APIs where clients send null for 'no change'; code that mixes $set semantics (null allowed) with $inc semantics (null forbidden); sparse test fixtures.","solutions":["Omit the key entirely instead of setting null/undefined: delete update.$inc.count when the value is nullish","Convert null to 0 when you mean 'no change': { $inc: { count: value ?? 0 } }","Use $set or $unset when you actually intend to clear the field","Strip null/undefined values from $inc/$pop objects before executing the update"],"exampleFix":"// before\nconst inc = { count: req.body.count }; // count may be null\nawait Model.updateOne({ _id }, { $inc: inc });\n\n// after\nconst inc = {};\nif (req.body.count != null) inc.count = Number(req.body.count);\nawait Model.updateOne({ _id }, { $inc: inc });","handlingStrategy":"validation","validationCode":"// Strip nullish operands from numeric update operators\nfunction cleanNumberOps(update) {\n  for (const op of ['$inc', '$pop']) {\n    if (!update[op]) continue;\n    for (const k of Object.keys(update[op])) {\n      if (update[op][k] == null) delete update[op][k];\n    }\n    if (Object.keys(update[op]).length === 0) delete update[op];\n  }\n  return update;\n}","typeGuard":"const hasNumberOperand = (update, op) => Object.values(update[op] ?? {}).every(v => v != null && Number.isFinite(Number(v)));","tryCatchPattern":"try {\n  await Model.updateOne(f, { $inc: { count } });\n} catch (err) {\n  if (err instanceof mongoose.Error.CastError && err.kind === 'number') {\n    // treat as client error: missing/absent increment value\n  } else throw err;\n}","preventionTips":["Treat null increments as 'no change': omit the key or use ?? 0","Use $set/$unset when the intent is to clear a field","Sanitize request bodies before mapping them onto update operators"],"tags":["mongoose","update","cast-error","inc","null-handling"],"backgroundTag":"type-cast-failed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}