{"record":{"id":"abc181dac2b3cdd3","repo":"RocketChat/Rocket.Chat","slug":"error-unit-cant-be-empty","errorCode":"error-unit-cant-be-empty","errorMessage":"The last department in a unit can't be removed","messagePattern":"The last department in a unit can't be removed","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/departmentsLib.ts","lineNumber":45,"sourceCode":"\t},\n\tdepartmentUnit?: { _id?: string },\n) {\n\tif (departmentUnit?._id !== undefined && typeof departmentUnit._id !== 'string') {\n\t\tthrow new Meteor.Error('error-invalid-department-unit', 'Invalid department unit id provided', {\n\t\t\tmethod: 'livechat:saveDepartment',\n\t\t});\n\t}\n\n\tconst department = _id\n\t\t? await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id' | 'archived' | 'enabled' | 'parentId'>>(_id, {\n\t\t\t\tprojection: { _id: 1, archived: 1, enabled: 1, parentId: 1 },\n\t\t\t})\n\t\t: null;\n\n\tif (departmentUnit && !departmentUnit._id && department && department.parentId) {\n\t\tconst isLastDepartmentInUnit = (await LivechatDepartment.countDepartmentsInUnit(department.parentId)) === 1;\n\t\tif (isLastDepartmentInUnit) {\n\t\t\tthrow new Meteor.Error('error-unit-cant-be-empty', \"The last department in a unit can't be removed\", {\n\t\t\t\tmethod: 'livechat:saveDepartment',\n\t\t\t});\n\t\t}\n\t}\n\n\tif (!department && !(await isDepartmentCreationAvailable())) {\n\t\tthrow new Meteor.Error('error-max-departments-number-reached', 'Maximum number of departments reached', {\n\t\t\tmethod: 'livechat:saveDepartment',\n\t\t});\n\t}\n\n\tif (department?.archived && departmentData.enabled) {\n\t\tthrow new Meteor.Error('error-archived-department-cant-be-enabled', 'Archived departments cant be enabled', {\n\t\t\tmethod: 'livechat:saveDepartment',\n\t\t});\n\t}\n\n\t// TODO: Use AJV or Zod for validation (or the lib we are using rn)","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/departmentsLib.ts#L27-L63","documentation":"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.","triggerScenarios":"PUT /livechat/departments/:id with departmentUnit: { _id: '' } (or omitted id) on a department whose current unit has exactly one department — this one.","commonSituations":"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.","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"],"exampleFix":"// before\nawait saveDepartment(userId, deptId, data, agents, { _id: '' }); // last dept in unit -> blocked\n\n// after\n// step 1: give the unit another department\nawait saveDepartment(userId, otherDeptId, otherData, otherAgents, { _id: unitId });\n// step 2: now the removal succeeds\nawait saveDepartment(userId, deptId, data, agents, { _id: '' });","handlingStrategy":"validation","validationCode":"if (departmentUnit && !departmentUnit._id && existingDepartment?.parentId) {\n  const count = await LivechatDepartment.countDepartmentsInUnit(existingDepartment.parentId);\n  if (count === 1) throw new Error('Move another department into the unit before removing this one');\n}\nawait saveDepartment(userId, _id, departmentData, departmentAgents, departmentUnit);","typeGuard":"const wouldEmptyUnit = (currentUnitId: string | undefined, countInUnit: number): boolean =>\n  currentUnitId !== undefined && countInUnit === 1;","tryCatchPattern":"try {\n  await saveDepartment(userId, _id, data, agents, { _id: '' });\n} catch (err) {\n  if (err instanceof Meteor.Error && (err as any).error === 'error-unit-cant-be-empty') {\n    // assign a replacement department to the unit (or a new unit to this one), then retry\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["omnichannel","livechat","departments","units","business-rule","precondition"],"backgroundTag":"last-child-deletion-blocked","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}