{"record":{"id":"81679c40246fe37c","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-81679c","errorCode":"error-invalid-room","errorMessage":"error-invalid-room","messagePattern":"error-invalid-room","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/messages/processWebhookMessage.ts","lineNumber":176,"sourceCode":"\n\tif (messageObj.attachments && !Array.isArray(messageObj.attachments)) {\n\t\tSystemLogger.warn({\n\t\t\tmsg: 'Attachments should be Array, ignoring value',\n\t\t\tattachments: messageObj.attachments,\n\t\t});\n\t\tmessageObj.attachments = undefined;\n\t}\n\n\tconst message = buildMessage(messageObj, defaultValues);\n\n\tfor (const channel of channels) {\n\t\tconst channelType = channel[0];\n\t\tconst channelValue = channel.slice(1);\n\t\tlet room: IRoom | null = null;\n\t\ttry {\n\t\t\troom = await getRoomWithOptionToJoin(channelType, channelValue, user);\n\t\t\tif (!room) {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\t\t\tawait validateRoomMessagePermissionsAsync(room, { uid: user._id, ...user });\n\t\t\tawait validateWebhookMessage(message, room, user);\n\t\t\trooms.push({ room, channel });\n\t\t} catch (_error: any) {\n\t\t\tif (messageObj.separateResponse) {\n\t\t\t\tconst { error, message } = _error || {};\n\t\t\t\tconst errorMessage = error || message || 'unknown-error';\n\t\t\t\trooms.push({ error: errorMessage, room, channel });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow _error;\n\t\t}\n\t}\n\n\tfor (const roomData of rooms) {\n\t\tif ('error' in roomData && roomData.error) {\n\t\t\tif (messageObj.separateResponse) {","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/messages/processWebhookMessage.ts#L158-L194","documentation":"Defensive guard in processWebhookMessage (processWebhookMessage.ts:171-191): after getRoomWithOptionToJoin resolves a channel target, a null room (possible via the errorOnEmpty:false lookup paths, e.g. type mismatch or a room vanishing mid-resolution) throws Error 'error-invalid-room'. In practice this line and the surrounding per-channel try/catch also capture the room-resolution and permission errors ('invalid-channel', message-size, room permission) for each channel: with separateResponse they are recorded per channel; otherwise the first error aborts the whole call.","triggerScenarios":"The room lookup returns null without throwing (type mismatch paths with errorOnEmpty:false, room deleted between the two lookups); more commonly, a downstream error in the same try block - validateRoomMessagePermissionsAsync denying the webhook user or validateWebhookMessage rejecting size - surfaces through the same handling.","commonSituations":"Bulk webhook sends to multiple channels where one channel is inaccessible to the integration user; channels deleted mid-send; webhook user removed from private rooms; integrations wanting partial success instead of all-or-nothing.","solutions":["Pre-verify each target channel exists and is postable by the webhook/integration user before sending","Set separateResponse: true so failures are reported per channel in the response items instead of aborting everything","Inspect the per-item error field of the response to find which channel failed and why","Keep channel names stable or update integration configs immediately after renames/deletions"],"exampleFix":"// before: single bad channel aborts the whole webhook call\n// after: { \"channel\": \"#a,#b,#gone\", \"separateResponse\": true, ... }\n// response: [{channel:'#a', message},{channel:'#b', message},{channel:'#gone', error:'error-invalid-room'}]","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const result = await processWebhookMessage({ ...payload, separateResponse: true }, user, defaultValue);\nfor (const item of result) {\n  if (item.error) {\n    logChannelFailure(item.channel, item.error); // per-channel: continue with the rest\n  }\n}","preventionTips":["Prefer separateResponse:true so one bad channel never aborts a batch","Always inspect per-item error fields in webhook responses","Keep the integration user's room memberships in sync with target channels"],"tags":["webhook","integration","room","incoming-webhook","error-handling"],"backgroundTag":"room-not-found","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"}