{"record":{"id":"ea1340fc4f84c2d3","repo":"RocketChat/Rocket.Chat","slug":"error-transcript-already-requested","errorCode":"error-transcript-already-requested","errorMessage":"error-transcript-already-requested","messagePattern":"error-transcript-already-requested","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/lib/omnichannel/sendTranscript.ts","lineNumber":240,"sourceCode":"\trid,\n\temail,\n\tsubject,\n\tuser,\n}: {\n\trid: string;\n\temail: string;\n\tsubject: string;\n\tuser: AtLeast<IUser, '_id' | 'username' | 'utcOffset' | 'name'>;\n}) {\n\t// `v` is required by the MAC-limit check below\n\tconst room = await LivechatRooms.findOneById(rid, { projection: { _id: 1, open: 1, transcriptRequest: 1, v: 1 } });\n\n\tif (!room?.open) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room');\n\t}\n\n\tif (room.transcriptRequest) {\n\t\tthrow new Meteor.Error('error-transcript-already-requested', 'Transcript already requested');\n\t}\n\n\tif (!(await Omnichannel.isWithinMACLimit(room))) {\n\t\tthrow new Error('error-mac-limit-reached');\n\t}\n\n\tconst { _id, username, name, utcOffset } = user;\n\tconst transcriptRequest = {\n\t\trequestedAt: new Date(),\n\t\trequestedBy: {\n\t\t\t_id,\n\t\t\tusername,\n\t\t\tname,\n\t\t\tutcOffset,\n\t\t},\n\t\temail,\n\t\tsubject,\n\t};","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2a7de457074cbb4d4373fbd9a4e5bea292c9c764/apps/meteor/server/lib/omnichannel/sendTranscript.ts#L222-L258","documentation":"requestTranscript stores a pending request in room.transcriptRequest and only allows one at a time: if the field is already set (a previous transcript request has not been fulfilled/cleared yet) it throws Meteor.Error('error-transcript-already-requested', 'Transcript already requested'). The field is cleared when the transcript email is sent when the room closes.","triggerScenarios":"Calling requestTranscript twice for the same room before the first request is processed — double-click on the button, two agents requesting a transcript for the same ongoing conversation, or a client retry that ignores the first success.","commonSituations":"Double-submit from the UI, aggressive API retries without idempotency, or the pending request never clearing because the closing/transcript send flow failed.","solutions":["Treat this code as an expected idempotency signal in the UI: show 'transcript already requested' instead of an error","If the pending request is stale (room never closed, transcript never sent), inspect room.transcriptRequest and clear it after confirming nothing will send","Debounce/disable the request button after the first successful call","For API callers, check room.transcriptRequest before POSTing"],"exampleFix":"// before\nawait requestTranscript({ rid, email, subject, user }); // second call throws\n\n// after\nconst room = await LivechatRooms.findOneById(rid, { projection: { open: 1, transcriptRequest: 1 } });\nif (room?.open && !room.transcriptRequest) {\n  await requestTranscript({ rid, email, subject, user });\n}","handlingStrategy":"validation","validationCode":"import { LivechatRooms } from '@rocket.chat/models';\n\nconst room = await LivechatRooms.findOneById(rid, { projection: { open: 1, transcriptRequest: 1 } });\nif (room?.transcriptRequest) {\n  return { alreadyRequested: true, pending: room.transcriptRequest }; // idempotent success\n}\nawait requestTranscript({ rid, email, subject, user });","typeGuard":null,"tryCatchPattern":"try {\n  await requestTranscript({ rid, email, subject, user });\n} catch (err) {\n  if (err instanceof Meteor.Error && err.error === 'error-transcript-already-requested') {\n    // treat as success-for-the-first-request: inform user a transcript is already queued\n  } else {\n    throw err;\n  }\n}","preventionTips":["Disable the transcript button after the first successful request until the room closes","Make API retries idempotent: check transcriptRequest before re-POSTing","Watch for stale transcriptRequest fields that never cleared and clean them during room audits"],"tags":["omnichannel","transcript","duplicate-request","idempotency"],"backgroundTag":"duplicate-request","analyzedSha":"2a7de457074cbb4d4373fbd9a4e5bea292c9c764","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}