{"record":{"id":"bf147d1dffa00b58","repo":"RocketChat/Rocket.Chat","slug":"you-can-t-send-messages-because-the-room-is-readon","errorCode":null,"errorMessage":"You can't send messages because the room is readonly.","messagePattern":"You can't send messages because the room is readonly\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/authorization/canSendMessage.ts","lineNumber":45,"sourceCode":"\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\n\tif (args.username && room?.muted?.includes(args.username)) {\n\t\tthrow new Error('You_have_been_muted');\n\t}\n}\n// TODO: remove option uid and username and type\nexport async function canSendMessageAsync(\n\trid: IRoom['_id'],\n\tuser: { uid: IUser['_id']; username: IUser['username']; type: IUser['type'] } | IUser,\n\textraData?: Record<string, any>,\n): Promise<IRoom> {\n\tconst room = await Rooms.findOneById(rid);\n\tif (!room) {\n\t\tthrow new Error('error-invalid-room');\n\t}\n","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/authorization/canSendMessage.ts#L27-L63","documentation":"The send-path readonly guard: validateRoomMessagePermissionsAsync throws this plain Error when room.ro === true, the user lacks the 'post-readonly' permission in that room, and their username is not in room.unmuted. Read-only rooms accept posts only from unmuted users or holders of post-readonly (typically owners and moderators).","triggerScenarios":"chat.sendMessage / chat.postMessage / UI send in a read-only channel as a regular member who has not been unmuted by a moderator.","commonSituations":"Announcement channels that are read-only by design; read-only toggled on during an incident while responders were never unmuted; default roles lacking post-readonly.","solutions":["Grant 'post-readonly' to the role that must speak in read-only rooms (Administration -> Permissions)","Unmute the specific users in the read-only room (adds them to room.unmuted)","Turn read-only off when the restriction is no longer needed","In clients, disable the composer when room.ro is set for users without post-readonly, so the error never fires"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const canSpeak =\n  room.ro !== true ||\n  (await hasPermissionAsync(uid, 'post-readonly', room._id)) ||\n  (room.unmuted || []).includes(username);\nif (!canSpeak) {\n  // disable the composer for this room instead of letting the send fail\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sendMessage(...);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('room is readonly')) {\n    // permanent state: inform the user the channel is read-only\n  }\n  throw e;\n}","preventionTips":["Disable the composer client-side when room.ro is set without post-readonly","When toggling read-only during incidents, unmute responders at the same time","Keep role grants of post-readonly aligned with moderation policy"],"tags":["authorization","messages","readonly-room","permissions"],"backgroundTag":"readonly-room-write-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}