meteor/meteor · error · MiniMongoQueryError

$or/$and/$nor entries need to be full objects

Error message

$or/$and/$nor entries need to be full objects

What it means

Each element of an $and/$or/$nor array must itself be a full document selector (a plain object), checked with LocalCollection._isPlainObject. This guards the .map in compileArrayOfDocumentSelectors, ensuring each branch is a valid sub-selector rather than a scalar, array, or class instance. MongoDB applies the same rule: every array entry is interpreted as a separate query document.

Source

Thrown at packages/minimongo/common.js:561

      delete match.distance;
      delete match.arrayIndices;
    }

    return match;
  };
}

const andDocumentMatchers = andSomeMatchers;
const andBranchedMatchers = andSomeMatchers;

function compileArrayOfDocumentSelectors(selectors, matcher, inElemMatch) {
  if (!Array.isArray(selectors) || selectors.length === 0) {
    throw new MiniMongoQueryError('$and/$or/$nor must be nonempty array');
  }

  return selectors.map(subSelector => {
    if (!LocalCollection._isPlainObject(subSelector)) {
      throw new MiniMongoQueryError('$or/$and/$nor entries need to be full objects');
    }

    return compileDocumentSelector(subSelector, matcher, {inElemMatch});
  });
}

// Takes in a selector that could match a full document (eg, the original
// selector). Returns a function mapping document->result object.
//
// matcher is the Matcher object we are compiling.
//
// If this is the root document selector (ie, not wrapped in $and or the like),
// then isRoot is true. (This is used by $near.)
export function compileDocumentSelector(docSelector, matcher, options = {}) {
  const docMatchers = Object.keys(docSelector).map(key => {
    const subSelector = docSelector[key];

    if (key.substr(0, 1) === '$') {

View on GitHub (pinned to 5076d2f818)

Solutions

  1. Wrap each scalar entry as a document: map ids to {_id: id} before $or.
  2. Validate with selectors.every(s => LocalCollection._isPlainObject(s)) before querying.
  3. Re-check the shape of any selector coming from JSON.parse or an EJSON payload.

Example fix

// before
db.find({ $or: idList });
// after
db.find({ $or: idList.map(id => ({ _id: id })) });
Defensive patterns

Strategy: validation

Validate before calling

function normalizeOr(items) {
  if (!items.every(x => x && typeof x === 'object' && !Array.isArray(x))) {
    throw new TypeError('$or entries must be plain objects');
  }
  return items;
}
db.find({ $or: normalizeOr(rawList) });

Type guard

function isPlainObjectList(arr) {
  return Array.isArray(arr) && arr.every(
    x => x !== null && Object.getPrototypeOf(x) === Object.prototype);
}

Prevention

When it happens

Trigger: Passing {$or: [1, 2]}, {$or: ['foo']}, {$and: [{a:1}, [1,2]]}, or an array containing a Date / ObjectId / EJSON-serialized value that is not a plain object.

Common situations: Spreading a mixed list into $or where some entries are raw ids instead of {_id: id} objects, or de-serializing a selector whose array elements lost their plain-object prototype.

Related errors


AI-assisted analysis of meteor/meteor@5076d2f818 (2026-08-13). Data as JSON: /api/errors/8b45cc1812a82314. Report an issue: GitHub.