{"record":{"id":"6f41ccbfd2de0255","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-6f41cc","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/unfollowMessage.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 { unfollow } 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\tunfollowMessage(message: { mid: IMessage['_id'] }): false | undefined;\n\t}\n}\n\nexport const unfollowMessage = 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: 'unfollowMessage' });\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: 'unfollowMessage',\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: 'unfollowMessage' });\n\t}\n\n\tconst id = message.tmid || message._id;\n\n\tconst unfollowResult = await unfollow({ rid: message.rid, 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/unfollowMessage.ts#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Enable Threads_enabled or hide the unfollow UI when the setting is off","Migrate to POST /v1/chat.unfollowMessage, the supported replacement surface","Skip the call client-side when threads are disabled - it can never succeed"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// client: skip unfollow when threads are off\nconst threadsEnabled = settings.get('Threads_enabled');\nif (threadsEnabled) {\n\tawait Meteor.callAsync('unfollowMessage', { mid });\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('unfollowMessage', { mid });\n} catch (e: any) {\n\tif (e?.error === 'error-not-allowed') {\n\t\t// threads disabled: clear the follow state locally, no retry\n\t}\n\tthrow e;\n}","preventionTips":["Hide follow/unfollow UI when Threads_enabled is false","Migrate to POST /v1/chat.unfollowMessage (the method is deprecated in 9.0.0)","Re-read the setting after workspace setting changes, not only at boot"],"tags":["meteor-method","threads","settings","deprecated"],"backgroundTag":"feature-flag-disabled","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}