RocketChat/Rocket.Chat · error · Meteor.Error
error-unit-cant-be-empty
error-unit-cant-be-empty
Error message
The last department in a unit can't be removed
What it means
saveDepartment blocks removing a department's unit assignment (departmentUnit present with an empty _id) when that department is the only one left in its unit: countDepartmentsInUnit(parentId) === 1. Omnichannel units must contain at least one department, so you cannot detach the last child — the unit would become empty.
Source
Thrown at apps/meteor/server/lib/omnichannel/departmentsLib.ts:45
},
departmentUnit?: { _id?: string },
) {
if (departmentUnit?._id !== undefined && typeof departmentUnit._id !== 'string') {
throw new Meteor.Error('error-invalid-department-unit', 'Invalid department unit id provided', {
method: 'livechat:saveDepartment',
});
}
const department = _id
? await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id' | 'archived' | 'enabled' | 'parentId'>>(_id, {
projection: { _id: 1, archived: 1, enabled: 1, parentId: 1 },
})
: null;
if (departmentUnit && !departmentUnit._id && department && department.parentId) {
const isLastDepartmentInUnit = (await LivechatDepartment.countDepartmentsInUnit(department.parentId)) === 1;
if (isLastDepartmentInUnit) {
throw new Meteor.Error('error-unit-cant-be-empty', "The last department in a unit can't be removed", {
method: 'livechat:saveDepartment',
});
}
}
if (!department && !(await isDepartmentCreationAvailable())) {
throw new Meteor.Error('error-max-departments-number-reached', 'Maximum number of departments reached', {
method: 'livechat:saveDepartment',
});
}
if (department?.archived && departmentData.enabled) {
throw new Meteor.Error('error-archived-department-cant-be-enabled', 'Archived departments cant be enabled', {
method: 'livechat:saveDepartment',
});
}
// TODO: Use AJV or Zod for validation (or the lib we are using rn)View on GitHub (pinned to b2c16d5842)
Solutions
- First move or create another department into the unit (assign it departmentUnit._id = that unit), then retry removing this one
- Or, if the unit is being retired, reassign this department to a different unit in the same request (departmentUnit._id = newUnitId) instead of sending an empty id
- Model unit membership in admin tooling so a 'move out' always specifies the destination unit
Example fix
// before
await saveDepartment(userId, deptId, data, agents, { _id: '' }); // last dept in unit -> blocked
// after
// step 1: give the unit another department
await saveDepartment(userId, otherDeptId, otherData, otherAgents, { _id: unitId });
// step 2: now the removal succeeds
await saveDepartment(userId, deptId, data, agents, { _id: '' }); Defensive patterns
Strategy: validation
Validate before calling
if (departmentUnit && !departmentUnit._id && existingDepartment?.parentId) {
const count = await LivechatDepartment.countDepartmentsInUnit(existingDepartment.parentId);
if (count === 1) throw new Error('Move another department into the unit before removing this one');
}
await saveDepartment(userId, _id, departmentData, departmentAgents, departmentUnit); Type guard
const wouldEmptyUnit = (currentUnitId: string | undefined, countInUnit: number): boolean => currentUnitId !== undefined && countInUnit === 1;
Try / catch
try {
await saveDepartment(userId, _id, data, agents, { _id: '' });
} catch (err) {
if (err instanceof Meteor.Error && (err as any).error === 'error-unit-cant-be-empty') {
// assign a replacement department to the unit (or a new unit to this one), then retry
}
throw err;
} Prevention
- In admin tooling, make 'remove from unit' a move operation that requires a destination unit
- Check unit membership counts before bulk department reassignment
- Remember empty-id departmentUnit means 'detach from unit' and is only blocked for the last member
When it happens
Trigger: PUT /livechat/departments/:id with departmentUnit: { _id: '' } (or omitted id) on a department whose current unit has exactly one department — this one.
Common situations: Reorganizing a workspace: moving the last department of 'Sales' unit out without assigning a replacement; deleting/moving departments until one remains, then trying to detach it.
Related errors
- error-invalid-department-unit
- error-forwarding-department-target-not-allowed
- error-not-allowed
- error-contact-has-no-conflicts
- error-max-departments-number-reached
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/abc181dac2b3cdd3.
Report an issue: GitHub.