{"record":{"id":"0d51eb28010b4061","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-0d51eb","errorCode":null,"errorMessage":"error-not-allowed","messagePattern":"error-not-allowed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/authorization/canSendMessage.ts","lineNumber":30,"sourceCode":"\t\tblocker: 1,\n\t},\n};\n\n// TODO: remove option uid and username and type\nexport async function validateRoomMessagePermissionsAsync(\n\troom: IRoom | null,\n\targs: { uid: IUser['_id']; username: IUser['username']; type: IUser['type'] } | IUser,\n\textraData?: Record<string, any>,\n): Promise<void> {\n\tif (!room) {\n\t\tthrow new Error('error-invalid-room');\n\t}\n\n\tif (room.archived) {\n\t\tthrow new Error('room_is_archived');\n\t}\n\tif (args.type !== 'app' && !(await canAccessRoomAsync(room, 'uid' in args ? { _id: args.uid } : args, extraData))) {\n\t\tthrow new Error('error-not-allowed');\n\t}\n\n\tif (\n\t\tawait roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.BLOCK, 'uid' in args ? args.uid : args._id)\n\t) {\n\t\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, 'uid' in args ? args.uid : args._id, subscriptionOptions);\n\t\tif (subscription && (subscription.blocked || subscription.blocker)) {\n\t\t\tthrow new Error('room_is_blocked');\n\t\t}\n\t}\n\n\tif (room.ro === true && !(await hasPermissionAsync('uid' in args ? args.uid : args._id, 'post-readonly', room._id))) {\n\t\t// Unless the user was manually unmuted\n\t\tif (args.username && !(room.unmuted || []).includes(args.username)) {\n\t\t\tthrow new Error(\"You can't send messages because the room is readonly.\");\n\t\t}\n\t}\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/authorization/canSendMessage.ts#L12-L48","documentation":"validateRoomMessagePermissionsAsync throws error-not-allowed (plain Error) when the acting user (type !== 'app') fails canAccessRoomAsync(room, user, extraData) — the standard room access check: private channels and teams require membership, while public rooms are open. App users skip this check entirely.","triggerScenarios":"Sending to a private channel, team room, or DM the user is not a member of — chat.postMessage with a rid the account never joined, or after being removed from the room.","commonSituations":"Integrations posting to private channels without being invited; REST scripts reusing a rid after the account lost membership; race where the user is kicked mid-session.","solutions":["Add the user (or integration user) to the room: invite them, or channels.join for public channels","For integrations, grant membership first or post as a bot that was explicitly added to the channel","Derive valid rids from the caller's subscriptions instead of hardcoding them"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const membership = await Subscriptions.findOneByRoomIdAndUserId(room._id, uid, { projection: { _id: 1 } });\nif (room.t === 'p' && !membership) {\n  // private room and not a member: join/invite first instead of sending\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sendMessage({ rid, ... });\n} catch (e) {\n  if (e instanceof Error && e.message === 'error-not-allowed') {\n    // access denied for this room: request membership/invite; do not retry with same credentials\n  }\n  throw e;\n}","preventionTips":["Add integration users to private rooms explicitly before configuring them","Fetch valid rids from the acting user's subscriptions","Re-check membership after users are removed from teams/channels"],"tags":["authorization","rooms","membership","permissions"],"backgroundTag":"room-access-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}