meteor/meteor · error · Error

Please specify only one of removed() and removedAt()

Error message

Please specify only one of removed() and removedAt()

What it means

Thrown by LocalCollection._observeCallbacksAreOrdered when both 'removed' (unordered) and 'removedAt' (ordered, with index) are present on the observe callbacks object. Same mutually-exclusive ordered/unordered contract as the added and changed pairs.

Source

Thrown at packages/minimongo/local_collection.js:1695

  if (Meteor._isPromise(handle)) {
    handle.then(setSuppressed);
  } else {
    setSuppressed(handle);
  }
  return handle;
};

LocalCollection._observeCallbacksAreOrdered = callbacks => {
  if (callbacks.added && callbacks.addedAt) {
    throw new Error('Please specify only one of added() and addedAt()');
  }

  if (callbacks.changed && callbacks.changedAt) {
    throw new Error('Please specify only one of changed() and changedAt()');
  }

  if (callbacks.removed && callbacks.removedAt) {
    throw new Error('Please specify only one of removed() and removedAt()');
  }

  return !!(
    callbacks.addedAt ||
    callbacks.changedAt ||
    callbacks.movedTo ||
    callbacks.removedAt
  );
};

LocalCollection._observeChangesCallbacksAreOrdered = callbacks => {
  if (callbacks.added && callbacks.addedBefore) {
    throw new Error('Please specify only one of added() and addedBefore()');
  }

  return !!(callbacks.addedBefore || callbacks.movedBefore);
};

View on GitHub (pinned to 5076d2f818)

Solutions

  1. Pick ordered or unordered and keep only that set of remove callbacks.
  2. Keep removedAt(doc, idx) if you need to know the document's former position; otherwise keep removed(doc).
  3. Audit the callbacks object for all three pairs (added/addedAt, changed/changedAt, removed/removedAt) since the guard runs against each.
  4. Add a unit test asserting no conflicting callback pair is set.

Example fix

// before
users.find().observe({ removedAt(doc, idx) {}, removed(doc) {} });
// after
users.find().observe({ removedAt(doc, idx) {} });
Defensive patterns

Strategy: validation

Validate before calling

function assertNoRemovedPair(cb) {
  if (cb && cb.removed && cb.removedAt) {
    throw new TypeError('Choose removed (unordered) OR removedAt (ordered), not both');
  }
  return cb;
}
cursor.observe(assertNoRemovedPair(callbacks));

Type guard

function hasConflictingRemovedPair(cb) {
  return !!(cb && cb.removed && cb.removedAt);
}

Prevention

When it happens

Trigger: Passing cursor.observe({ removed() {...}, removedAt(doc, idx) {...} }) — both keys truthy at observe setup time triggers the synchronous guard.

Common situations: Copying a removed handler from an unordered example into an already-ordered observe callbacks object; auto-generating callbacks from a schema where both forms are defined.

Related errors


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