RocketChat/Rocket.Chat · error · Error

error-not-allowed

Error message

error-not-allowed

What it means

Thrown by PUT /livechat/department/:_id when the authenticated user lacks the `manage-livechat-departments` permission. IMPORTANT nuance: the route-level guard (departments.ts:117) allows PUT with hasAny(`manage-livechat-departments`, `add-livechat-department-agents`), but the action body re-checks (departments.ts:143-156) and requires `manage-livechat-departments` specifically. So a user holding only `add-livechat-department-agents` passes the route guard yet fails inside the handler. Returns HTTP 400 { success:false, error:'error-not-allowed' }.

Source

Thrown at apps/meteor/server/api/v1/omnichannel/departments.ts:156

			// to show the "new" view. Returning 404 breaks it

			return API.v1.success({ department, agents });
		},
		async put() {
			const permissionToSave = await hasPermissionAsync(this.user, 'manage-livechat-departments');
			const permissionToAddAgents = await hasPermissionAsync(this.user, 'add-livechat-department-agents');

			check(this.bodyParams, {
				department: Object,
				agents: Match.Maybe(Array),
				departmentUnit: Match.Maybe({ _id: Match.Optional(String) }),
			});

			const { _id } = this.urlParams;
			const { department, agents, departmentUnit } = this.bodyParams;

			if (!permissionToSave) {
				throw new Error('error-not-allowed');
			}

			const agentParam = permissionToAddAgents && agents ? { upsert: agents } : {};
			await saveDepartment(this.userId, _id, department, agentParam, departmentUnit || {});

			return API.v1.success({
				department: await LivechatDepartment.findOneById(_id),
				agents: await LivechatDepartmentAgents.findByDepartmentId(_id).toArray(),
			});
		},
		async delete() {
			check(this.urlParams, {
				_id: String,
			});

			const isRemoveEnabled = settings.get<boolean>('Omnichannel_enable_department_removal');

			if (!isRemoveEnabled) {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Grant the calling user/role the `manage-livechat-departments` permission.
  2. If the caller only needs to manage agents on a department, use POST /livechat/department/:_id/agents instead of PUT on the department itself.
  3. Reconcile the route-level vs action-level permission mismatch (the action over-constrains relative to the declared route guard) if the divergence is a bug.

Example fix

// before: role has only 'add-livechat-department-agents' -> PUT /livechat/department/:_id throws error-not-allowed

// after: assign the required permission to the role
await Roles.addPermissionRoles('manage-livechat-departments', ['livechat-manager']);
Defensive patterns

Strategy: validation

Validate before calling

const canManage = await hasPermissionAsync(user, 'manage-livechat-departments');
if (!canManage) {
  // route to POST /livechat/department/:_id/agents instead, or surface forbidden
  throw new Error('caller lacks manage-livechat-departments');
}

Type guard

const canManageDepartment = async (user: IUser) =>
  await hasPermissionAsync(user, 'manage-livechat-departments');

Try / catch

try {
  await putDepartment(_id, department, agents, departmentUnit);
} catch (e) {
  if (e instanceof Error && e.message === 'error-not-allowed') {
    // caller needs manage-livechat-departments; fall back to the agents endpoint
  } else { throw e; }
}

Prevention

When it happens

Trigger: PUT /livechat/department/:_id by a user/role that has `add-livechat-department-agents` but NOT `manage-livechat-departments`. The route guard admits the request, the action then rejects it.

Common situations: An agent-manager role intended only to add agents to a department tries to edit department settings; permission was recently revoked; role misconfiguration; the route/action permission divergence is unintended.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/3481c3c3a86c82c5. Report an issue: GitHub.