{"record":{"id":"02382e262460d9ee","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-02382e","errorCode":"error-not-allowed","errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/rooms/cleanRoomHistory.ts","lineNumber":45,"sourceCode":"}\n\nexport const cleanRoomHistoryMethod = async (\n\tuserId: string,\n\t{\n\t\troomId,\n\t\tlatest,\n\t\toldest,\n\t\tinclusive = true,\n\t\tlimit,\n\t\texcludePinned = false,\n\t\tignoreDiscussion = true,\n\t\tfilesOnly = false,\n\t\tfromUsers = [],\n\t\tignoreThreads,\n\t}: CleanRoomHistoryParams,\n): Promise<number> => {\n\tif (!(await hasPermissionAsync(userId, 'clean-channel-history', roomId))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'cleanRoomHistory' });\n\t}\n\n\tconst room = await findRoomByIdOrName({ params: { roomId } });\n\n\tif (!room || !(await canAccessRoomAsync(room, { _id: userId }))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'cleanRoomHistory' });\n\t}\n\n\treturn cleanRoomHistory({\n\t\trid: roomId,\n\t\tlatest,\n\t\toldest,\n\t\tinclusive,\n\t\tlimit,\n\t\texcludePinned,\n\t\tignoreDiscussion,\n\t\tfilesOnly,\n\t\tfromUsers,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/rooms/cleanRoomHistory.ts#L27-L63","documentation":"First guard in the cleanRoomHistory execution path: hasPermissionAsync(userId, 'clean-channel-history', roomId) returned false. Cleaning/pruning room history requires the clean-channel-history permission, evaluated against the target room - by default an admin/moderator-level grant.","triggerScenarios":"A user without clean-channel-history invokes the cleanRoomHistory method (or the prune-history UI); the permission exists globally but is not granted at the target room scope; role changes removed it after the UI was rendered.","commonSituations":"Moderators expected to prune but whose role lacks clean-channel-history; retention automations running under a normal user account; fresh workspaces where the permission was never assigned to custom roles.","solutions":["Grant clean-channel-history to the acting user's role (Administration > Permissions).","Run the prune as an admin or a role that holds the permission.","Pre-check the permission client-side and hide the prune/clean action when absent."],"exampleFix":"// before\nMeteor.call('cleanRoomHistory', roomId, latest, oldest);\n\n// after\nif (hasPermission(Meteor.userId(), 'clean-channel-history', roomId)) {\n  Meteor.call('cleanRoomHistory', roomId, latest, oldest);\n} else {\n  showToast('Cleaning history requires the clean-channel-history permission');\n}","handlingStrategy":"try-catch","validationCode":"if (!hasPermission(Meteor.userId(), 'clean-channel-history', roomId)) {\n  // do not offer or attempt history cleaning for this room\n}","typeGuard":null,"tryCatchPattern":"try {\n  const removed = await Meteor.callAsync('cleanRoomHistory', roomId, latest, oldest);\n} catch (e) {\n  if (e instanceof Meteor.Error && e.error === 'error-not-allowed') {\n    // caller lacks clean-channel-history - surface an admin-actionable message\n    showToast('Cleaning history requires the clean-channel-history permission');\n    return;\n  }\n  throw e;\n}","preventionTips":["Expose prune/clean UI only to roles holding clean-channel-history.","Scope the permission check to the exact room id being cleaned.","For retention jobs, run as an admin/service account that holds the permission globally."],"tags":["rocket-chat","meteor","ddp","clean-history","permissions","authorization"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}