RocketChat/Rocket.Chat · error · Meteor.Error

error-not-allowed

error-not-allowed

Error message

not-allowed

What it means

unfollowMessage throws error-not-allowed immediately when mid is provided and the Threads_enabled setting is false. Unfollowing is a thread concept, so with threads disabled workspace-wide the operation is rejected before any message lookup happens.

Source

Thrown at apps/meteor/server/meteor-methods/messages/unfollowMessage.ts:24

import { Meteor } from 'meteor/meteor';

import { RateLimiterClass as RateLimiter } from '../../lib/RateLimiter';
import { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';
import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { unfollow } from '../../lib/messaging/threads/functions';
import { notifyOnMessageChange } from '../../lib/notifyListener';
import { settings } from '../../settings';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		unfollowMessage(message: { mid: IMessage['_id'] }): false | undefined;
	}
}

export const unfollowMessage = async (user: IUser, { mid }: { mid: IMessage['_id'] }): Promise<false | undefined> => {
	if (mid && !settings.get('Threads_enabled')) {
		throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
	}

	const message = await Messages.findOneById(mid);
	if (!message) {
		throw new Meteor.Error('error-invalid-message', 'Invalid message', {
			method: 'unfollowMessage',
		});
	}

	if (!(await canAccessRoomIdAsync(message.rid, user._id))) {
		throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
	}

	const id = message.tmid || message._id;

	const unfollowResult = await unfollow({ rid: message.rid, tmid: id, uid: user._id });

	void notifyOnMessageChange({

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Enable Threads_enabled or hide the unfollow UI when the setting is off
  2. Migrate to POST /v1/chat.unfollowMessage, the supported replacement surface
  3. Skip the call client-side when threads are disabled - it can never succeed
Defensive patterns

Strategy: validation

Validate before calling

// client: skip unfollow when threads are off
const threadsEnabled = settings.get('Threads_enabled');
if (threadsEnabled) {
	await Meteor.callAsync('unfollowMessage', { mid });
}

Try / catch

try {
	await Meteor.callAsync('unfollowMessage', { mid });
} catch (e: any) {
	if (e?.error === 'error-not-allowed') {
		// threads disabled: clear the follow state locally, no retry
	}
	throw e;
}

Prevention

When it happens

Trigger: Clicking 'unfollow' in a stale UI after threads were disabled; replaying an old client build against a workspace where threads got turned off; calling the deprecated method (9.0.0 -> POST /v1/chat.unfollowMessage) in tests without seeding the setting.

Common situations: Admin disabled threads while clients kept thread UI cached; automation written when threads were on, reused after they were turned off; CI without the setting enabled.

Related errors


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