RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-department-unit

error-invalid-department-unit

Error message

Invalid department unit id provided

What it means

saveDepartment (POST/PUT /api/v1/livechat/departments) rejects the request when departmentUnit is provided with an _id that is defined but not a string — the guard is '_id !== undefined && typeof _id !== string'. So departmentUnit._id = null, 42, or an object fails, while omitting _id entirely is fine. It is a payload-shape error thrown as a Meteor.Error with method context 'livechat:saveDepartment'.

Source

Thrown at apps/meteor/server/lib/omnichannel/departmentsLib.ts:31

import { notifyOnLivechatDepartmentAgentChangedByDepartmentId, notifyOnLivechatDepartmentAgentChanged } from '../notifyListener';
/**
 * @param {string|null} _id - The department id
 * @param {Partial<import('@rocket.chat/core-typings').ILivechatDepartment>} departmentData
 * @param {{upsert?: { agentId: string; count?: number; order?: number; }[], remove?: { agentId: string; count?: number; order?: number; }}} [departmentAgents] - The department agents
 * @param {{_id?: string}} [departmentUnit] - The department's unit id
 */
export async function saveDepartment(
	userId: string,
	_id: string | null,
	departmentData: LivechatDepartmentDTO,
	departmentAgents?: {
		upsert?: { agentId: string; count?: number; order?: number }[];
		remove?: { agentId: string; count?: number; order?: number }[];
	},
	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',
			});
		}
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. To keep the current unit: omit departmentUnit._id (or the whole departmentUnit object)
  2. To assign a unit: send departmentUnit._id as the unit's string id
  3. Fix client serialization so empty means absent key, not null

Example fix

// before
{ name: 'Support', departmentUnit: { _id: null } }

// after
{ name: 'Support' } // no unit change
// or
{ name: 'Support', departmentUnit: { _id: 'unitId123' } }
Defensive patterns

Strategy: type-guard

Validate before calling

if (departmentUnit !== undefined) {
  if (departmentUnit._id !== undefined && typeof departmentUnit._id !== 'string') {
    throw new TypeError('departmentUnit._id must be a string unit id or absent');
  }
}
await saveDepartment(userId, _id, departmentData, departmentAgents, departmentUnit);

Type guard

type DepartmentUnit = { _id?: string };
const isDepartmentUnit = (v: unknown): v is DepartmentUnit =>
  v === undefined || (typeof v === 'object' && v !== null &&
    ((v as DepartmentUnit)._id === undefined || typeof (v as DepartmentUnit)._id === 'string'));

Try / catch

try {
  await saveDepartment(userId, _id, data, agents, departmentUnit);
} catch (err) {
  if (err instanceof Meteor.Error && (err as any).error === 'error-invalid-department-unit') {
    // strip null _id from departmentUnit or send the unit's string id, then retry
  }
  throw err;
}

Prevention

When it happens

Trigger: Sending JSON like { departmentUnit: { _id: null } } (a common 'unset' idiom from serializers) or a numeric/object _id to the department save endpoint; note the REST layer passes departmentUnit || {}, so an empty object is safe but an explicit null _id is not.

Common situations: Client frameworks serializing empty references as null; sending unit number instead of its string id; API clients built against docs where _id was assumed optional-and-null.

Related errors


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