Automattic/mongoose · error · ObjectExpectedError

Tried to set nested object field `${path}` to ${typeDescript

Error message

Tried to set nested object field `${path}` to ${typeDescription} `${val}`

What it means

During hydration Mongoose walks the schema's nested paths and expects an object at every nested level. ObjectExpectedError is thrown when the raw document stores a non-object (string, number, array) where the schema declares a nested sub-document; the message names the path, the offending type and the value.

Source

Thrown at lib/document.js:755

};

/**
 * Init helper.
 *
 * @param {object} self document instance
 * @param {object} obj raw mongodb doc
 * @param {object} doc object we are initializing
 * @param {object} [opts] Optional Options
 * @param {boolean} [opts.setters] Call `applySetters` instead of `cast`
 * @param {string} [prefix] Prefix to add to each path
 * @api private
 */

function init(self, obj, doc, opts, prefix) {
  prefix = prefix || '';

  if (typeof obj !== 'object' || Array.isArray(obj)) {
    throw new ObjectExpectedError(self.$basePath, obj);
  }

  if (obj.$__ != null) {
    obj = obj._doc;
  }
  const keys = Object.keys(obj);
  const len = keys.length;
  let schemaType;
  let path;
  let i;
  const strict = self.$__.strictMode;
  const docSchema = self.$__schema;
  const strictRead = docSchema.options.strictRead;

  for (let index = 0; index < len; ++index) {
    i = keys[index];
    // avoid prototype pollution
    if (specialProperties.has(i)) {

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Migrate the data so the path holds an object: Model.updateMany({ nested: { $type: 'string' } }, [{ $set: { nested: { sub: '$nested' } } }])
  2. If the flat value is legitimate, change the schema: declare the path with its scalar type or use Schema.Types.Mixed
  3. When importing untrusted data, validate nested paths before hydrating and route bad docs to a repair/dead-letter queue

Example fix

// before — schema expects an object, data has a string
// schema: new Schema({ nested: { sub: String } }); db doc: { nested: 'oops' }
const doc = await Model.findOne(); // ObjectExpectedError

// after — migrate stored data to the schema shape
await Model.updateMany(
  { nested: { $type: 'string' } },
  [{ $set: { nested: { sub: '$nested' } } }]
);
const doc = await Model.findOne();
Defensive patterns

Strategy: validation

Validate before calling

function nestedPathsOk(schema, raw) {
  const nested = Object.entries(schema.tree)
    .filter(([p, t]) => raw[p] != null && t != null && typeof t === 'object' && !Array.isArray(t))
    .map(([p]) => p);
  return nested.every(p => typeof raw[p] === 'object');
}
// before doc.init(raw) on untrusted data:
if (!nestedPathsOk(Model.schema, raw)) {
  deadLetter(raw);
} else {
  doc.init(raw);
}

Try / catch

try {
  await Model.findOne({ _id });
} catch (err) {
  if (err.name === 'ObjectExpectedError') {
    // stored data shape disagrees with the schema — inspect and migrate the doc
    await quarantineDoc(_id, err.path);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Schema new Schema({ nested: { sub: String } }) while the stored document holds nested: 'oops' (or a number/array); the error surfaces when hydrating via findOne/find or doc.init(raw).

Common situations: Schema refactors that turned a scalar field into a nested object without a data migration; hand-edited or imported data; another writer (legacy app, different ORM) storing flat values into the same collection.

Related errors


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