{"record":{"id":"db6241169df6d1d7","repo":"Automattic/mongoose","slug":"cast-to-objectid-failed-for-value-value-type","errorCode":null,"errorMessage":"Cast to ObjectId failed for value \"${value}\" (type ${valueType}) at path \"${path}\"","messagePattern":"Cast to ObjectId failed for value \"(.+?)\" \\(type (.+?)\\) at path \"(.+?)\"","errorType":"validation","errorClass":"CastError","httpStatus":null,"severity":"error","filePath":"lib/schema/objectId.js","lineNumber":252,"sourceCode":"\n    if (value == null || utils.isNonBuiltinObject(value)) {\n      return this._castRef(value, doc, init, options);\n    }\n  }\n\n  let castObjectId;\n  if (typeof this._castFunction === 'function') {\n    castObjectId = this._castFunction;\n  } else if (typeof this.constructor.cast === 'function') {\n    castObjectId = this.constructor.cast();\n  } else {\n    castObjectId = SchemaObjectId.cast();\n  }\n\n  try {\n    return castObjectId(value);\n  } catch (error) {\n    throw new CastError('ObjectId', value, this.path, error, this);\n  }\n};\n\n/*!\n * ignore\n */\n\nfunction handleSingle(val) {\n  return this.cast(val);\n}\n\nconst $conditionalHandlers = {\n  ...SchemaType.prototype.$conditionalHandlers,\n  $gt: handleSingle,\n  $gte: handleSingle,\n  $lt: handleSingle,\n  $lte: handleSingle\n};","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/schema/objectId.js#L234-L270","documentation":"SchemaObjectId.cast wraps the ObjectId caster (lib/cast/objectid.js), which passes through real ObjectIds, plucks `value._id` when present, and otherwise calls `new ObjectId(value.toString())`. That bson constructor throws for any string that is not 24 hex characters (or 12 bytes), and mongoose wraps the failure as CastError 'ObjectId' — the wrapped form of bson's 'Argument passed in must be a single String of 12 bytes or a string of 24 hex characters'.","triggerScenarios":"`doc.owner = 'abc'` (wrong length), `doc._id = '507f1f77bcf86cd7994d90'` (23 chars), `{ owner: { $in: ['xyz', ...] } }`, assigning UUIDs or arbitrary slugs to an ObjectId path, or an object whose toString() yields a non-hex string.","commonSituations":"Route params (`/users/:id`) receiving garbage or empty strings without validation; ids from external systems (UUID, ULID, ints) assigned to ObjectId fields; truncated ids from logs; case-sensitive hex issues (uppercase hex actually works — length is the usual culprit); populated docs re-serialized to strings with quotes.","solutions":["Validate first: `if (!mongoose.isValidObjectId(id)) return 400` — check shape before touching the model","Convert explicitly at the boundary inside try/catch: `new mongoose.Types.ObjectId(id)` and reject on throw","If you accept non-ObjectIds (UUIDs etc.), change the path type to String","In bulk $in filters, filter the array first: `ids.filter(mongoose.isValidObjectId)`"],"exampleFix":"// before\nconst user = await User.findById(req.params.id); // 'abc' → CastError\n\n// after\nif (!mongoose.isValidObjectId(req.params.id)) {\n  return res.status(400).json({ error: 'invalid id' });\n}\nconst user = await User.findById(req.params.id);","handlingStrategy":"type-guard","validationCode":"if (!mongoose.isValidObjectId(id)) {\n  throw Object.assign(new Error('invalid id'), { status: 400 });\n}\nconst doc = await Model.findById(id);","typeGuard":"function isObjectIdLike(v) {\n  return v == null || v instanceof mongoose.Types.ObjectId ||\n    (typeof v === 'string' && /^[a-f0-9]{24}$/i.test(v));\n}","tryCatchPattern":"try { doc.owner = id; } catch (err) { if (err.name === 'CastError' && err.kind === 'ObjectId') { return badRequest(`invalid ObjectId for ${err.path}`); } throw err; }","preventionTips":["Validate route params with mongoose.isValidObjectId before querying","Filter $in arrays: ids.filter(mongoose.isValidObjectId)","Use String paths for UUID/ULID identifiers instead of ObjectId"],"tags":["mongoose","objectid","cast","validation","route-params"],"backgroundTag":"mongoose-cast-error","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}