{"record":{"id":"faaa4d9b5ec2434c","repo":"RocketChat/Rocket.Chat","slug":"room-still-open","errorCode":null,"errorMessage":"room-still-open","messagePattern":"room-still-open","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/lib/omnichannel/requestPdfTranscript.ts","lineNumber":17,"sourceCode":"import { OmnichannelTranscript, QueueWorker } from '@rocket.chat/core-services';\nimport type { AtLeast, IOmnichannelRoom } from '@rocket.chat/core-typings';\nimport ExpiryMap from 'expiry-map';\n\nimport { logger } from './logger';\n\n// Allow to request a transcript again after 15 seconds, assuming the first one didn't complete\n// This won't prevent multiple transcript generated for the same room in a multi-instance deployment since state is not shared, but we're ok with the drawbacks\nconst LockMap = new ExpiryMap<string, boolean>(15000);\n\nconst serviceName = 'omnichannel-transcript' as const;\nexport const requestPdfTranscript = async (\n\troom: AtLeast<IOmnichannelRoom, '_id' | 'open' | 'v' | 'pdfTranscriptFileId'>,\n\trequestedBy: string,\n): Promise<void> => {\n\tif (room.open) {\n\t\tthrow new Error('room-still-open');\n\t}\n\n\tif (!room.v) {\n\t\tthrow new Error('improper-room-state');\n\t}\n\n\t// Don't request a transcript if there's already one requested\n\tif (LockMap.has(room._id) || room.pdfTranscriptFileId) {\n\t\t// TODO: use logger\n\t\tlogger.info({ msg: `Transcript already requested`, roomId: room._id });\n\t\treturn;\n\t}\n\n\tLockMap.set(room._id, true);\n\n\tconst details = { details: { rid: room._id, userId: requestedBy, from: serviceName } };\n\t// Make the whole process sync when running on test mode\n\t// This will prevent the usage of timeouts on the tests of this functionality :)","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/lib/omnichannel/requestPdfTranscript.ts#L1-L35","documentation":"Thrown by requestPdfTranscript in requestPdfTranscript.ts:17 when the supplied room is still open (room.open truthy). PDF transcripts are only generated for closed omnichannel rooms; requesting one on an open room is a contract violation. NOTE: plain `new Error('room-still-open')`.","triggerScenarios":"Calling requestPdfTranscript before the chat was closed (CloseRoom), or right after a close that has not yet flushed to the room record being passed in. Passing a freshly-constructed room object whose open field defaults true.","commonSituations":"Race between client closing the room and triggering transcript; webhook fired on a 'transcript requested' event for a still-open room; UI lets the user click 'get transcript' too early.","solutions":["Ensure the room is closed (room.open === false) before invoking requestPdfTranscript.","If close is async, wait for the close callback / room-updated subscription before requesting.","Match by message 'room-still-open' and retry once the close settles."],"exampleFix":"// before\nawait requestPdfTranscript(room, requestedBy);\n\n// after\nif (room.open) {\n  await closeRoom(room._id); // ensure settled\n  room = await Rooms.findOneById(room._id);\n}\nawait requestPdfTranscript(room, requestedBy);","handlingStrategy":"validation","validationCode":"if (room.open) {\n  // ensure the close has settled\n  await closeRoom(room._id);\n  room = await Rooms.findOneById(room._id);\n}","typeGuard":"const isRoomClosed = (room: { open?: boolean }) => room.open === false;","tryCatchPattern":"try { await requestPdfTranscript(room, requestedBy); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'room-still-open') { /* close first */ return; }\n  throw e;\n}","preventionTips":["Wait for the close callback before requesting a transcript.","Disable the transcript action while room.open is true."],"tags":["omnichannel","pdf-transcript","room-state","plain-error"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}