Automattic/mongoose · error · CastError

Cast to ${this.instance} failed for value "${value}" (type $

Error message

Cast to ${this.instance} failed for value "${value}" (type ${valueType}) at path "${this.path}"

What it means

CastError thrown from SchemaType.prototype.cast() when a path that is currently populated is assigned a non-object value (a primitive, string, number, or Buffer). A populated path must hold a document of the populated model, so scalars cannot be cast; without populate the same value (e.g. an ObjectId string) would cast fine.

Source

Thrown at lib/schemaType.js:1713

 * ignore
 */

SchemaType.prototype._castRef = function _castRef(value, doc, init, options) {
  if (value == null) {
    return value;
  }

  if (value.$__ != null) {
    value.$__.wasPopulated = value.$__.wasPopulated || { value: value._doc._id };
    return value;
  }

  // setting a populated path
  if (Buffer.isBuffer(value) || !utils.isObject(value)) {
    if (init) {
      return value;
    }
    throw new CastError(this.instance, value, this.path, null, this);
  }

  // Handle the case where user directly sets a populated
  // path to a plain object; cast to the Model used in
  // the population query.
  const path = doc.$__fullPath(this.path, true);
  const owner = doc.ownerDocument();
  const pop = owner.$populated(path, true);

  let ret = value;
  if (!doc.$__.populated ||
    !doc.$__.populated[path] ||
    !doc.$__.populated[path].options ||
    !doc.$__.populated[path].options.options ||
    !doc.$__.populated[path].options.options.lean) {
    const PopulatedModel = pop ? pop.options[populateModelSymbol] : owner.constructor.db.model(this.options.ref);
    ret = PopulatedModel.hydrate(value, null, options);
    ret.$__.wasPopulated = { value: ret._doc._id, options: { [populateModelSymbol]: PopulatedModel } };

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Assign an object with the _id: doc.owner = { _id: ownerId }
  2. Or depopulate first, then set the scalar: doc.depopulate('owner'); doc.owner = ownerId;
  3. Or assign a real model instance: doc.owner = await User.findById(ownerId)

Example fix

// before
await doc.populate('owner');
doc.owner = '64b1f0c2e4b0a1d2c3e4f5a6'; // CastError
// after
await doc.populate('owner');
doc.owner = { _id: '64b1f0c2e4b0a1d2c3e4f5a6' };
Defensive patterns

Strategy: try-catch

Validate before calling

function setRef(doc, path, value) {
  if (doc.$populated && doc.$populated(path) && (typeof value !== 'object' || value === null || Buffer.isBuffer(value))) {
    doc.depopulate(path);
  }
  doc.set(path, value);
}

Type guard

function isPopulatedDocValue(value) {
  return value != null && typeof value === 'object' && !Buffer.isBuffer(value);
}

Try / catch

try { doc.owner = rawId; } catch (err) { if (err.name === 'CastError' && err.path === 'owner') { doc.owner = { _id: rawId }; } else throw err; }

Prevention

When it happens

Trigger: doc.populate('owner').then(() => { doc.owner = '64b1...'; }) — assigning a raw id string to a populated ref path; doc.set('owner', 5) or assigning a Buffer while the path is populated.

Common situations: Trying to reset a populated reference back to an _id after populate; assigning values parsed from JSON/web requests onto populated documents; spreading request bodies onto populated docs.

Related errors


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