RocketChat/Rocket.Chat · error · Meteor.Error
error-unit-not-found
error-unit-not-found
Error message
Unit not found
What it means
Thrown by saveUnit in LivechatEnterprise.ts:111 when editing an existing business unit (_id supplied) but the unit cannot be found within the caller's scoped set (unitsFromUser). It is a guard before reading ancestors for the update. Code is 'error-unit-not-found' (note the different prefix from 240) via Meteor.Error with method 'livechat:saveUnit'.
Source
Thrown at apps/meteor/ee/server/lib/omnichannel/LivechatEnterprise.ts:111
check(unitDepartments, [
Match.ObjectIncluding({
departmentId: String,
}),
]);
let ancestors: string[] = [];
if (_id) {
const unitsFromUser = await getUnitsFromUser(userId);
const unit = await LivechatUnit.findOneById<Pick<IOmnichannelBusinessUnit, '_id' | 'ancestors'>>(
_id,
{
projection: { _id: 1, ancestors: 1 },
},
{ unitsFromUser },
);
if (!unit) {
throw new Meteor.Error('error-unit-not-found', 'Unit not found', {
method: 'livechat:saveUnit',
});
}
ancestors = unit.ancestors || [];
}
const validUserMonitors = await Users.findUsersInRolesWithQuery(
'livechat-monitor',
{ _id: { $in: unitMonitors.map(({ monitorId }) => monitorId) } },
{ projection: { _id: 1, username: 1 } },
).toArray();
const monitors = validUserMonitors.map(({ _id: monitorId, username }) => ({
monitorId,
username: username!,
}));
View on GitHub (pinned to f9d3ec372b)
Solutions
- Re-fetch the unit list for the current userId and use a fresh _id.
- Confirm the user is assigned as a monitor of the target unit before allowing edit.
- Catch Meteor.Error 'error-unit-not-found' and surface a 'unit no longer available' message rather than retrying blindly.
Example fix
// before
await saveUnit(_id, unitData, monitors, depts, userId);
// after
const mine = await getUnitsFromUser(userId);
if (!_id || !mine.includes(_id)) {
throw new Error('refresh unit list; _id not in user scope');
}
await saveUnit(_id, unitData, monitors, depts, userId); Defensive patterns
Strategy: validation
Validate before calling
if (_id) {
const mine = await getUnitsFromUser(userId);
const unit = await LivechatUnit.findOneById(_id, { projection: { _id: 1 } }, { unitsFromUser: mine });
if (!unit) throw new Error('refresh; unit not in scope');
} Type guard
const canEditUnit = async (_id: string, userId: string): Promise<boolean> => {
const mine = await getUnitsFromUser(userId);
return Boolean(await LivechatUnit.findOneById(_id, { projection: { _id: 1 } }, { unitsFromUser: mine }));
}; Try / catch
try { await saveUnit(_id, unitData, monitors, depts, userId); }
catch (e) {
if (e instanceof Meteor.Error && e.error === 'error-unit-not-found') { /* refresh UI */ return; }
throw e;
} Prevention
- Confirm the user is a monitor of the target unit before opening the edit form.
- Refetch the unit when the form gains focus.
When it happens
Trigger: Calling livechat:saveUnit with a stale _id after the unit was deleted, or with a userId not assigned as a monitor of that unit. The scoped findOne (unitsFromUser) returns null even if the unit exists globally.
Common situations: Editing a business unit in a long-open form whose row was removed elsewhere; cross-tenant / cross-unit admin editing a unit they do not monitor; race between saveUnit and removeUnit.
Related errors
- unit-not-found
- error-unit-not-found
- error-not-authorized-federation
- error-forwarding-department-target-not-allowed
- error-invalid-sla
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/25e317b901d4e30f.
Report an issue: GitHub.