RocketChat/Rocket.Chat · error · Meteor.Error

not-authorized

not-authorized

Error message

Not Authorized

What it means

The final gate in executeSetReaction: canAccessRoomAsync(room, user) must pass or Meteor.Error('not-authorized', 'Not Authorized') is thrown. The room exists but the caller is not allowed to see it (not a member of the private channel/discussion/DM). Distinct from 'error-not-allowed': this is an authorization failure, not a missing resource.

Source

Thrown at apps/meteor/server/lib/messaging/reactions/setReaction.ts:139

	const userAlreadyReacted = Boolean(message.reactions?.[reaction]?.usernames?.includes(user.username as string));

	// When shouldReact was not informed, toggle the reaction.
	if (shouldReact === undefined) {
		shouldReact = !userAlreadyReacted;
	}

	if (userAlreadyReacted === shouldReact) {
		return;
	}

	const room = await Rooms.findOneById(message.rid);
	if (!room) {
		throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
	}

	if (!(await canAccessRoomAsync(room, user))) {
		throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'setReaction' });
	}

	return setReaction(room, user, message, reaction, userAlreadyReacted);
}

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		setReaction(reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): boolean | undefined;
	}
}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Ensure the acting user/bot has an active membership (subscription) in the room before triggering reactions
  2. Refresh room subscriptions after removal events and close stale room views
  3. For apps, add the bot to the private channel before reacting on its behalf

Example fix

// before - bot reacts right after losing membership
await executeSetReaction(botId, 'tada', messageId);

// after - verify membership first
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, botId, { projections: { _id: 1 } });
if (sub) {
  await executeSetReaction(botId, 'tada', messageId);
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the method's own gate before delegating
if (!(await canAccessRoomAsync(room, user))) {
  return forbidden(room._id);
}
await executeSetReaction(user._id, 'tada', messageId);

Try / catch

Meteor.call('setReaction', 'tada', messageId, (err) => {
  if (err?.error === 'not-authorized') {
    closeRoomAndResubscribe(rid); // membership is gone - stop interacting with the room
  }
});

Prevention

When it happens

Trigger: Reacting in a room the user was removed from while their UI stayed stale; an Apps Engine bot user that was never added to a private channel; a guest without access to the room type; a DM whose counterpart blocked or deleted the account.

Common situations: User kicked from a private channel but the tab/session kept the room open; bots reacting via apps in channels where they lost membership; kick and react racing each other.

Related errors


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