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
- To keep the current unit: omit departmentUnit._id (or the whole departmentUnit object)
- To assign a unit: send departmentUnit._id as the unit's string id
- 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
- Serialize 'no unit' as an omitted key, never as _id: null
- Type the request DTO so _id is string | undefined, not string | null
- Add a request-schema check (AJV/zod) in clients that call department endpoints
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
- error-not-allowed
- error-unit-cant-be-empty
- error-room-does-not-exist
- invalid-param
- error-forwarding-department-target-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/0a0398de2772c761.
Report an issue: GitHub.