RocketChat/Rocket.Chat · error · Error

error-not-authorized

Error message

error-not-authorized

What it means

Thrown by findBusinessHours in business-hour.ts:14 when the caller lacks the 'view-livechat-business-hours' permission (checked via hasPermissionAsync). This is the omnichannel business-hour list/retrieval helper used by REST handlers. NOTE: plain `new Error('error-not-authorized')` — the canonical Rocket.Chat authorization code is in the message, not as a Meteor error code.

Source

Thrown at apps/meteor/ee/server/lib/omnichannel/business-hour/lib/business-hour.ts:14

import type { ILivechatBusinessHour } from '@rocket.chat/core-typings';
import { LivechatBusinessHours, LivechatDepartment } from '@rocket.chat/models';
import { escapeRegExp } from '@rocket.chat/string-helpers';

import { hasPermissionAsync } from '../../../../../../server/lib/authorization/hasPermission';
import type { IPaginatedResponse, IPagination } from '../../../../api/v1/omnichannel/lib/definition';

interface IResponse extends IPaginatedResponse {
	businessHours: ILivechatBusinessHour[];
}

export async function findBusinessHours(userId: string, { offset, count, sort }: IPagination, name?: string): Promise<IResponse> {
	if (!(await hasPermissionAsync(userId, 'view-livechat-business-hours'))) {
		throw new Error('error-not-authorized');
	}
	const query = {};
	if (name) {
		const filterReg = new RegExp(escapeRegExp(name), 'i');
		Object.assign(query, { name: filterReg });
	}
	const { cursor, totalCount } = LivechatBusinessHours.findPaginated(query, {
		sort: sort || { name: 1 },
		skip: offset,
		limit: count,
	});

	const [businessHours, total] = await Promise.all([cursor.toArray(), totalCount]);

	// add departments to businessHours
	const businessHoursWithDepartments = await Promise.all(
		businessHours.map(async (businessHour) => {
			const currentDepartments = await LivechatDepartment.findByBusinessHourId(businessHour._id, {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Grant the calling user/role the 'view-livechat-business-hours' permission.
  2. Verify the authenticated userId before invoking the helper and return 403 explicitly.
  3. Match error by message 'error-not-authorized' (no structured code).

Example fix

// before
const res = await findBusinessHours(userId, pagination, name);

// after
if (!(await hasPermissionAsync(userId, 'view-livechat-business-hours'))) {
  return res.status(403).send({ error: 'not-authorized' });
}
const res = await findBusinessHours(userId, pagination, name);
Defensive patterns

Strategy: validation

Validate before calling

if (!(await hasPermissionAsync(userId, 'view-livechat-business-hours'))) {
  return res.status(403).send({ error: 'not-authorized' });
}

Type guard

const canViewBH = (userId: string) => hasPermissionAsync(userId, 'view-livechat-business-hours');

Try / catch

try { await findBusinessHours(userId, pagination, name); }
catch (e) {
  if (e instanceof Error && e.message === 'error-not-authorized') return res.status(403).send({ error: 'not-authorized' });
  throw e;
}

Prevention

When it happens

Trigger: Any REST/route that delegates to findBusinessHours being invoked by a user without 'view-livechat-business-hours': a non-agent, an omnichannel manager without the view permission, or an unauthenticated/bot token lacking the role.

Common situations: Custom integration token missing the livechat-manager or admin role; new role created without inheriting view-livechat-business-hours; permission revoked by an admin.

Related errors


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