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

  1. First move or create another department into the unit (assign it departmentUnit._id = that unit), then retry removing this one
  2. 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
  3. 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

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


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