RocketChat/Rocket.Chat · error · Error

error-not-authorized

Error message

error-not-authorized

What it means

Thrown inside applyDepartmentRestrictions (inquiries.ts:27-29) when an agent is department-restricted (they have one or more department assignments) and the requested `department` filter is not among their allowed departments. The allowed list is built from LivechatDepartmentAgents (by agentId) filtered through LivechatDepartment.findEnabledInIds. Returns HTTP 400 { success:false, error:'error-not-authorized' }.

Source

Thrown at apps/meteor/server/api/v1/omnichannel/lib/inquiries.ts:28

const agentDepartments = async (userId: IUser['_id']): Promise<string[]> => {
	const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray()).map(
		({ departmentId }) => departmentId,
	);
	return (await LivechatDepartment.findEnabledInIds(agentDepartments, { projection: { _id: 1 } }).toArray()).map(({ _id }) => _id);
};

const applyDepartmentRestrictions = async (
	userId: IUser['_id'],
	filterDepartment?: string,
): Promise<{ $in: string[] } | { $exists: false } | string> => {
	const allowedDepartments = await agentDepartments(userId);
	if (allowedDepartments && Array.isArray(allowedDepartments) && allowedDepartments.length > 0) {
		if (!filterDepartment) {
			return { $in: allowedDepartments };
		}

		if (!allowedDepartments.includes(filterDepartment)) {
			throw new Error('error-not-authorized');
		}
		return filterDepartment;
	}

	return { $exists: false };
};

export async function findInquiries({
	userId,
	department: filterDepartment,
	status,
	pagination: { offset, count, sort },
}: {
	userId: IUser['_id'];
	department?: string;
	status?: LivechatInquiryStatus;
	pagination: { offset: number; count: number; sort: Record<string, number> };
}): Promise<PaginatedResult<{ inquiries: Array<ILivechatInquiryRecord> }>> {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Omit the `department` filter so the server auto-scopes to the agent's own departments (the code returns { $in: allowedDepartments }).
  2. Request a department the agent actually belongs to.
  3. Grant the agent membership in the requested department (insert into LivechatDepartmentAgents) if cross-department visibility is intended.

Example fix

// before
findInquiries({ userId, department: 'deptB-not-mine', ... }); // throws error-not-authorized

// after
findInquiries({ userId, /* department omitted */ ... }); // auto-scoped to agent's departments
Defensive patterns

Strategy: validation

Validate before calling

const allowed = (await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray())
  .map((d) => d.departmentId);
if (filterDepartment && !allowed.includes(filterDepartment)) {
  // drop the filter (auto-scope) or pick an allowed department
  filterDepartment = undefined;
}

Type guard

const isAgentDepartment = async (userId: string, deptId: string) => {
  const set = new Set((await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray()).map((d) => d.departmentId));
  return set.has(deptId);
};

Try / catch

try {
  await findInquiries({ userId, department: filterDepartment, ... });
} catch (e) {
  if (e instanceof Error && e.message === 'error-not-authorized') {
    // retry without the department filter to auto-scope to allowed departments
    await findInquiries({ userId, /* no department */ ... });
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling findInquiries (e.g. GET /livechat/inquiries) with a `department` query param naming a department the agent is not a member of, while the agent has at least one other department assignment.

Common situations: Agent scoped to Department A queries Department B's queue; department membership changed but the client keeps a cached department id; multi-tenant isolation enforced via department agents.

Related errors


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