{"record":{"id":"686d68fb67f4b822","repo":"RocketChat/Rocket.Chat","slug":"you-cant-leave-a-livechat-room-please-use-the-clos","errorCode":null,"errorMessage":"You_cant_leave_a_livechat_room_Please_use_the_close_button","messagePattern":"You_cant_leave_a_livechat_room_Please_use_the_close_button","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/lib/omnichannel/startup.ts","lineNumber":77,"sourceCode":"\t\tawait ContactMerger.mergeFieldsIntoContact({\n\t\t\tfields,\n\t\t\tcontact,\n\t\t\tconflictHandlingMode: contact.unknown ? 'overwrite' : 'conflict',\n\t\t});\n\t}\n\n\treturn visitor;\n});\n\nMeteor.startup(async () => {\n\troomCoordinator.setRoomFind('l', async (id) => maybeMigrateLivechatRoom(await LivechatRooms.findOneById(id)));\n\n\tbeforeLeaveRoomCallback.add(\n\t\t(user, room) => {\n\t\t\tif (!isOmnichannelRoom(room)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Meteor.Error(\n\t\t\t\ti18n.t('You_cant_leave_a_livechat_room_Please_use_the_close_button', {\n\t\t\t\t\tlng: user.language || settings.get('Language') || 'en',\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tcallbacks.priority.LOW,\n\t\t'cant-leave-omnichannel-room',\n\t);\n\n\tcallbacks.add(\n\t\t'beforeJoinRoom',\n\t\tasync (user, room) => {\n\t\t\tif (isOmnichannelRoom(room) && !(await hasPermissionAsync(user, 'view-l-room'))) {\n\t\t\t\tthrow new Meteor.Error('error-user-is-not-agent', 'User is not an Omnichannel Agent', {\n\t\t\t\t\tmethod: 'beforeJoinRoom',\n\t\t\t\t});\n\t\t\t}\n","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/startup.ts#L59-L95","documentation":"A beforeLeaveRoomCallback registered at Meteor.startup blocks the generic 'leave room' action for omnichannel rooms: if isOmnichannelRoom(room) is true it throws a Meteor.Error whose message is the translated key 'You_cant_leave_a_livechat_room_Please_use_the_close_button' (translated into the user's language). Livechat rooms are closed, not left — leaving would corrupt the agent/visitor relationship model.","triggerScenarios":"Any code path that ends in the 'leaveRoom' Meteor method/callback chain for a room with t: 'l' — e.g. a client calling leaveRoom on an omnichannel room, or a generic UI component reusing the channel-leave flow for livechat.","commonSituations":"Custom apps or UI components assuming all rooms support 'leave', users pressing a leave shortcut on a livechat conversation, or integrations calling Meteor.call('leaveRoom', rid) generically.","solutions":["For livechat rooms use the omnichannel close/return flows (the close button, or the room close APIs) instead of leaveRoom","Guard client-side: hide/disable the leave action when room.t === 'l'","If an agent should no longer handle the chat, close the conversation or transfer it — never leave","Catch this specific translated error in generic leave UIs and show the proper close affordance"],"exampleFix":"// before\nMeteor.call('leaveRoom', room._id); // throws for livechat rooms\n\n// after\nif (room.t === 'l') {\n  // omnichannel rooms are closed, not left\n  Meteor.call('livechat:closeRoom', room._id);\n} else {\n  Meteor.call('leaveRoom', room._id);\n}","handlingStrategy":"validation","validationCode":"// generic leave flow: branch on room type before calling leaveRoom\nfunction canLeaveRoom(room: { t: string }): boolean {\n  return room.t !== 'l'; // omnichannel rooms cannot be left, only closed\n}","typeGuard":"function isOmnichannelRoom(v: { t?: string } | null | undefined): v is { _id: string; t: 'l' } {\n  return v?.t === 'l';\n}","tryCatchPattern":"try {\n  await Meteor.callAsync('leaveRoom', rid);\n} catch (err) {\n  if (err instanceof Meteor.Error && /livechat_room/i.test(err.reason ?? '')) {\n    // show 'use the close button' guidance instead of a generic error\n  } else {\n    throw err;\n  }\n}","preventionTips":["Never wire the channel 'leave' action to livechat rooms — check room.t first","Hide leave affordances in omnichannel context; offer close/transfer instead","For apps-engine bots, use omnichannel-specific APIs rather than generic room membership calls"],"tags":["omnichannel","leave-room","room-lifecycle","i18n"],"backgroundTag":"operation-not-permitted","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"}