{"record":{"id":"4b9ca7f1ecde30a7","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-4b9ca7","errorCode":"error-not-allowed","errorMessage":"not-allowed","messagePattern":"not-allowed","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/followMessage.ts","lineNumber":24,"sourceCode":"import { Meteor } from 'meteor/meteor';\n\nimport { RateLimiterClass as RateLimiter } from '../../lib/RateLimiter';\nimport { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';\nimport { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\nimport { follow } from '../../lib/messaging/threads/functions';\nimport { notifyOnMessageChange } from '../../lib/notifyListener';\nimport { settings } from '../../settings';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tfollowMessage(message: { mid: IMessage['_id'] }): false | undefined;\n\t}\n}\n\nexport const followMessage = async (user: IUser, { mid }: { mid: IMessage['_id'] }): Promise<false | undefined> => {\n\tif (mid && !settings.get('Threads_enabled')) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });\n\t}\n\n\tconst message = await Messages.findOneById(mid);\n\tif (!message) {\n\t\tthrow new Meteor.Error('error-invalid-message', 'Invalid message', {\n\t\t\tmethod: 'followMessage',\n\t\t});\n\t}\n\n\tif (!(await canAccessRoomIdAsync(message.rid, user._id))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });\n\t}\n\n\tconst id = message.tmid || message._id;\n\n\tconst followResult = await follow({ tmid: id, uid: user._id });\n\n\tvoid notifyOnMessageChange({","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/messages/followMessage.ts#L6-L42","documentation":"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.","triggerScenarios":"Calling followMessage({ mid }) (or REST equivalents) on a workspace where an administrator disabled the Threads setting.","commonSituations":"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.","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"],"exampleFix":"// before\nMeteor.call('followMessage', { mid });\n\n// after — only offer follow when threads are enabled\nif (settings.get('Threads_enabled')) {\n  Meteor.call('followMessage', { mid });\n}","handlingStrategy":"validation","validationCode":"if (!settings.get('Threads_enabled')) {\n  // hide follow/unfollow affordances entirely\n} else {\n  Meteor.call('followMessage', { mid });\n}","typeGuard":null,"tryCatchPattern":"try {\n  await Meteor.callAsync('followMessage', { mid });\n} catch (e) {\n  if ((e as Meteor.Error).error === 'error-not-allowed') {\n    // threads disabled workspace-wide: hide the follow UI, do not retry\n  }\n}","preventionTips":["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"],"tags":["meteor","threads","settings","feature-flag"],"backgroundTag":"feature-disabled-by-config","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}