{"record":{"id":"6fcf2b7d6e76d66b","repo":"RocketChat/Rocket.Chat","slug":"error-action-not-allowed-6fcf2b","errorCode":"error-action-not-allowed","errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/messages/deleteMessage.ts","lineNumber":25,"sourceCode":"import { settings } from '../../settings';\nimport { canDeleteMessageAsync } from '../authorization/canDeleteMessage';\nimport { callbacks } from '../callbacks';\nimport { FileUpload } from '../media/file-upload';\nimport { notifyOnRoomChangedById, notifyOnMessageChange, notifyOnSubscriptionChangedByRoomIdAndUserIds } from '../notifyListener';\n\nexport const deleteMessageValidatingPermission = async (message: AtLeast<IMessage, '_id'>, userId: IUser['_id']): Promise<void> => {\n\tif (!message?._id) {\n\t\tthrow new Meteor.Error('error-invalid-message', 'Invalid message');\n\t}\n\tif (!userId) {\n\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user');\n\t}\n\n\tconst user = await Users.findOneById(userId);\n\tconst originalMessage = await Messages.findOneById(message._id);\n\n\tif (!originalMessage || !user || !(await canDeleteMessageAsync(user, originalMessage))) {\n\t\tthrow new Meteor.Error('error-action-not-allowed', 'Not allowed');\n\t}\n\n\treturn deleteMessage(originalMessage, user);\n};\n\nexport async function deleteMessage(message: IMessage, user: IUser): Promise<void> {\n\tconst deletedMsg: IMessage | null = await Messages.findOneById(message._id);\n\tconst isThread = (deletedMsg?.tcount || 0) > 0;\n\tconst keepHistory = settings.get('Message_KeepHistory') || isThread;\n\tconst showDeletedStatus = settings.get('Message_ShowDeletedStatus') || isThread;\n\n\tconst room = await Rooms.findOneById(message.rid, { projection: { lastMessage: 1, prid: 1, mid: 1, federated: 1, federation: 1 } });\n\n\tif (deletedMsg) {\n\t\tconst prevent = await Apps.self?.triggerEvent(AppEvents.IPreMessageDeletePrevent, deletedMsg);\n\t\tif (prevent) {\n\t\t\tthrow new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the message deleting.');\n\t\t}","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/messages/deleteMessage.ts#L7-L43","documentation":"The catch-all authorization failure in deleteMessageValidatingPermission (deleteMessage.ts:20-27): thrown when the original message cannot be found, the user cannot be found, or canDeleteMessageAsync returns false. That helper (authorization/canDeleteMessage.ts) denies when the room is gone/inaccessible, the user lacks force-delete-message, Message_AllowDeleting is off, the user has neither delete-message nor delete-own-message permission for the room, or the Message_AllowDeleting_BlockDeleteInMinutes window has elapsed.","triggerScenarios":"Deleting someone else's message without the room-scoped delete-message permission; Message_AllowDeleting disabled server-wide; the delete window (Message_AllowDeleting_BlockDeleteInMinutes) expired; the message id no longer exists (already deleted or wrong id); the user lost access to the room.","commonSituations":"Regular users trying to delete moderators' messages; tight compliance configs with a 1-minute delete window; deleting in rooms the user left or was removed from; double-delete races where the first delete already removed the message.","solutions":["Verify the message still exists (Messages.findOneById) before deleting - it may already be gone","Check the acting user's room permissions: delete-own-message for own messages, delete-message for others', force-delete-message/bypass-time-limit-edit-and-delete to override limits","Review Message_AllowDeleting and Message_AllowDeleting_BlockDeleteInMinutes settings if legit deletes fail","Handle the race: treat 'already deleted' as success in idempotent clients"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await deleteMessageValidatingPermission({ _id }, userId);\n} catch (error: any) {\n  if (error instanceof Meteor.Error && error.error === 'error-action-not-allowed') {\n    // check: message still exists? user has delete-message/delete-own-message?\n    // Message_AllowDeleting on? delete window elapsed?\n    showCannotDeleteMessage(_id);\n    return;\n  }\n  throw error;\n}","preventionTips":["Pre-check delete-own-message/delete-message permissions in the UI before showing the delete action","Respect Message_AllowDeleting_BlockDeleteInMinutes in client timers","Treat repeat failures as permission drift and re-check role assignments"],"tags":["messages","delete","permissions","authorization","settings"],"backgroundTag":"message-delete-permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}