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
- Ensure the acting user/bot has an active membership (subscription) in the room before triggering reactions
- Refresh room subscriptions after removal events and close stale room views
- 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
- Treat not-authorized as a membership failure - resync subscriptions, never retry
- Bots and app users must be room members even when acting via Apps Engine
- Invalidate room UIs upon subscription-remove stream events
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
- not-authorized
- error-invalid-room
- error-not-allowed
- error-not-allowed
- You can't send messages because the room is readonly.
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/9ad4978e02d88003.
Report an issue: GitHub.