RocketChat/Rocket.Chat · error · Meteor.Error

unit-not-found

unit-not-found

Error message

Unit not found

What it means

Thrown by removeUnit in LivechatEnterprise.ts:63 when a business-unit deletion affects zero documents. The handler first narrows the allowed set to units the operator is permitted to manage (getUnitsFromUser), then deletes by _id AND that scope; if neither the _id nor any scoped unit matches, deletedCount is 0. This is a Meteor.Error, so the code 'unit-not-found' and the {method:'livechat:removeUnit'} detail travel to the client.

Source

Thrown at apps/meteor/ee/server/lib/omnichannel/LivechatEnterprise.ts:63

		if (!(await removeUserFromRolesAsync(user._id, ['livechat-monitor']))) {
			return false;
		}

		// remove this monitor from any unit it is assigned to
		await LivechatUnitMonitors.removeByMonitorId(user._id);

		return true;
	},

	async removeUnit(_id: string, userId: string) {
		check(_id, String);

		const unitsFromUser = await getUnitsFromUser(userId);

		const result = await LivechatUnit.removeByIdAndUnit(_id, unitsFromUser);
		if (!result.deletedCount) {
			throw new Meteor.Error('unit-not-found', 'Unit not found', { method: 'livechat:removeUnit' });
		}

		return result;
	},

	async saveUnit(
		_id: string | null,
		unitData: Omit<IOmnichannelBusinessUnit, '_id'>,
		unitMonitors: { monitorId: string; username: string },
		unitDepartments: { departmentId: string }[],
		userId: string,
	) {
		check(_id, Match.Maybe(String));

		check(unitData, {
			name: String,
			visibility: String,
			enabled: Match.Optional(Boolean),

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Refresh the business-unit list and confirm the _id still exists for the current user before retrying.
  2. Verify the calling userId actually appears in getUnitsFromUser for that unit (permissions/monitor assignment).
  3. Handle the meteor error by code 'unit-not-found' and treat it as already-gone (idempotent success) if appropriate.

Example fix

// before
await removeUnit(_id, userId);

// after
try {
  await removeUnit(_id, userId);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'unit-not-found') {
    // already removed; refresh list and continue
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const mine = await getUnitsFromUser(userId);
const exists = mine.includes(_id);
// or: await LivechatUnit.findOneById(_id, { projection: { _id: 1 } }, { unitsFromUser: mine });

Type guard

const isUnitRemovable = async (_id: string, userId: string): Promise<boolean> => {
  const mine = await getUnitsFromUser(userId);
  return mine.includes(_id);
};

Try / catch

try { await removeUnit(_id, userId); }
catch (e) {
  if (e instanceof Meteor.Error && e.error === 'unit-not-found') return; // idempotent
  throw e;
}

Prevention

When it happens

Trigger: Calling livechat:removeUnit with an _id that does not exist, or with a userId whose getUnitsFromUser set does not contain that unit (e.g. an omnichannel manager scoped to other business units). Also fires if the unit was concurrently deleted between lookup and removal.

Common situations: UI list went stale and the row was already removed; user belongs to a different business unit than the one they are trying to delete; test fixtures did not seed the unit for that userId; multi-tab scenario where another admin removed it first.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/f57af31522618755. Report an issue: GitHub.