RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
not-allowed
What it means
followMessage throws 'error-not-allowed' ('not-allowed') when the workspace setting Threads_enabled is false — the very first guard in the method. Following a thread only makes sense with threads enabled, so the server short-circuits before looking up the message. This is a configuration gate, not a per-user permission check.
Source
Thrown at apps/meteor/server/meteor-methods/messages/followMessage.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 { follow } 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 {
followMessage(message: { mid: IMessage['_id'] }): false | undefined;
}
}
export const followMessage = 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: 'followMessage' });
}
const message = await Messages.findOneById(mid);
if (!message) {
throw new Meteor.Error('error-invalid-message', 'Invalid message', {
method: 'followMessage',
});
}
if (!(await canAccessRoomIdAsync(message.rid, user._id))) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });
}
const id = message.tmid || message._id;
const followResult = await follow({ tmid: id, uid: user._id });
void notifyOnMessageChange({View on GitHub (pinned to b2c16d5842)
Solutions
- Enable Threads: set Threads_enabled to true (Administration settings, or via the settings API) if thread following should work
- Gate the follow/unfollow UI on the Threads_enabled setting the server exposes to the client
- Once enabled, prefer the deprecation target POST /v1/chat.followMessage over this method
Example fix
// before
Meteor.call('followMessage', { mid });
// after — only offer follow when threads are enabled
if (settings.get('Threads_enabled')) {
Meteor.call('followMessage', { mid });
} Defensive patterns
Strategy: validation
Validate before calling
if (!settings.get('Threads_enabled')) {
// hide follow/unfollow affordances entirely
} else {
Meteor.call('followMessage', { mid });
} Try / catch
try {
await Meteor.callAsync('followMessage', { mid });
} catch (e) {
if ((e as Meteor.Error).error === 'error-not-allowed') {
// threads disabled workspace-wide: hide the follow UI, do not retry
}
} Prevention
- Derive thread-related UI affordances from the settings the server exposes
- Re-render when the Threads_enabled setting changes (settings update events)
- Treat 'not-allowed' from followMessage as a capability signal, not a transient failure
When it happens
Trigger: Calling followMessage({ mid }) (or REST equivalents) on a workspace where an administrator disabled the Threads setting.
Common situations: Workspaces that turn threads off to simplify UX; clients caching capabilities and still showing a 'follow thread' action; admin toggles the setting while clients are connected.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/4b9ca7f1ecde30a7.
Report an issue: GitHub.