{"record":{"id":"9c5413b282d333d7","repo":"RocketChat/Rocket.Chat","slug":"invalid-room-9c5413","errorCode":"invalid-room","errorMessage":"Invalid room","messagePattern":"Invalid room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/rooms/settings/saveRoomAnnouncement.ts","lineNumber":15,"sourceCode":"import { Message } from '@rocket.chat/core-services';\nimport type { IUser } from '@rocket.chat/core-typings';\nimport { Rooms } from '@rocket.chat/models';\nimport { Match } from 'meteor/check';\nimport { Meteor } from 'meteor/meteor';\nimport type { UpdateResult } from 'mongodb';\n\nexport const saveRoomAnnouncement = async function (\n\trid: string,\n\troomAnnouncement: string,\n\tuser: IUser,\n\tsendMessage = true,\n): Promise<UpdateResult> {\n\tif (!Match.test(rid, String)) {\n\t\tthrow new Meteor.Error('invalid-room', 'Invalid room', {\n\t\t\tfunction: 'RocketChat.saveRoomAnnouncement',\n\t\t});\n\t}\n\n\tlet message;\n\tlet announcementDetails;\n\tif (typeof roomAnnouncement === 'string') {\n\t\tmessage = roomAnnouncement;\n\t} else {\n\t\t({ message, ...announcementDetails } = roomAnnouncement);\n\t}\n\n\tconst updated = await Rooms.setAnnouncementById(rid, message, announcementDetails);\n\tif (updated && sendMessage) {\n\t\tawait Message.saveSystemMessage('room_changed_announcement', rid, message, user);\n\t}\n\n\treturn updated;","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/rooms/settings/saveRoomAnnouncement.ts#L1-L33","documentation":"Thrown by saveRoomAnnouncement when Match.test(rid, String) fails (saveRoomAnnouncement.ts:15). It is a pure argument-type gate in front of the announcement update — rid must be a string room _id; nothing is written when it throws. Code 'invalid-room', details { function: 'RocketChat.saveRoomAnnouncement' }.","triggerScenarios":"Calling saveRoomAnnouncement(undefined, text, user) because rid came from a form param that was never bound; passing the room name instead of the _id; passing a Mongo.ObjectId object instead of its string form.","commonSituations":"REST handlers forwarding unvalidated body fields; UI code using room.name where room._id was intended; migration code that assumed ids stay numbers.","solutions":["Pass the room's _id string (room._id), not its name or an ObjectId instance","Validate at the boundary: typeof rid === 'string' && rid.length > 0 before calling","Log the received rid at the API entry point to catch missing form/body fields","Write tests that call the function with a real rid to catch signature drift"],"exampleFix":"// before\nawait saveRoomAnnouncement(roomId, announcement, user); // roomId may be undefined\n\n// after\nif (typeof roomId !== 'string' || roomId.length === 0) {\n\tthrow new Meteor.Error('invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomAnnouncement' });\n}\nawait saveRoomAnnouncement(roomId, announcement, user);","handlingStrategy":"type-guard","validationCode":"if (typeof rid !== 'string' || rid.length === 0) {\n\tthrow new Meteor.Error('invalid-room', 'Invalid room', { function: 'RocketChat.saveRoomAnnouncement' });\n}\nawait saveRoomAnnouncement(rid, announcement, user);","typeGuard":"const isRoomId = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":null,"preventionTips":["Bind the rid form field explicitly in settings UIs","Reject requests without a string rid at the handler with a 400","Keep variable naming consistent (rid everywhere) to avoid undefined leaking from renames"],"tags":["room-settings","argument-validation","meteor-check","announcement"],"backgroundTag":"invalid-argument-value","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}