{"record":{"id":"e50bd3b6f4cac069","repo":"RocketChat/Rocket.Chat","slug":"error-mac-limit-reached-e50bd3","errorCode":"error-mac-limit-reached","errorMessage":"error-mac-limit-reached","messagePattern":"error-mac-limit-reached","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/transcript.ts","lineNumber":49,"sourceCode":"\t\t\tPOST: isPOSTLivechatTranscriptRequestParams,\n\t\t},\n\t},\n\t{\n\t\tasync delete() {\n\t\t\tconst { rid } = this.urlParams;\n\t\t\tconst room = await LivechatRooms.findOneById<Pick<IOmnichannelRoom, 'open' | 'transcriptRequest' | 'v'>>(rid, {\n\t\t\t\tprojection: { open: 1, transcriptRequest: 1, v: 1 },\n\t\t\t});\n\n\t\t\tif (!room?.open) {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\t\t\tif (!room.transcriptRequest) {\n\t\t\t\tthrow new Error('error-transcript-not-requested');\n\t\t\t}\n\n\t\t\tif (!(await Omnichannel.isWithinMACLimit(room))) {\n\t\t\t\tthrow new Error('error-mac-limit-reached');\n\t\t\t}\n\n\t\t\tawait LivechatRooms.unsetEmailTranscriptRequestedByRoomId(rid);\n\n\t\t\treturn API.v1.success();\n\t\t},\n\t\tasync post() {\n\t\t\tconst { rid } = this.urlParams;\n\t\t\tconst { email, subject } = this.bodyParams;\n\n\t\t\tconst user = await Users.findOneById(this.userId, {\n\t\t\t\tprojection: { _id: 1, username: 1, name: 1, utcOffset: 1 },\n\t\t\t});\n\n\t\t\tif (!user) {\n\t\t\t\tthrow new Error('error-invalid-user');\n\t\t\t}\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/omnichannel/transcript.ts#L31-L67","documentation":"Thrown by GET livechat/room/:rid/transcript when the room is open and a transcript was requested, but the visitor's last activity month is not already counted AND the installation has reached its monthlyActiveContacts (MAC) license cap. isWithinMACLimit returns true on Community Edition (no license) and on Enterprise when the visitor's v.activity already includes the current YYYY-MM month; otherwise it checks the license gate. So this fires only on EE at or above the MAC ceiling for a brand-new contact month.","triggerScenarios":"Calling GET /api/v1/v1/livechat/room/:rid/transcript on an open omnichannel room whose visitor has not yet been active this calendar month, on an EE instance whose monthlyActiveContacts limit is exhausted. The check runs after the open + transcriptRequest guards, so those must already pass.","commonSituations":"EE trial or starter license hit its monthly contact cap mid-month; license downgraded from a tier with a higher MAC allowance; a burst of new omnichannel visitors in a new month pushed the count over the limit.","solutions":["Upgrade or apply an EE license with a higher monthlyActiveContacts allowance.","Wait for the calendar month to roll over (the MAC window is month-based UTC) so the cap resets.","Confirm the visitor's v.activity already contains the current YYYY-MM so the contact is counted as already-known rather than newly added.","Verify the license is valid and active in Administration > License; an invalidated license falls back to CE (no limit), so a persistently firing error suggests a partially-applied license."],"exampleFix":"// before\nconst room = await LivechatRooms.findOneById(rid, { projection: { open: 1, transcriptRequest: 1, v: 1 } });\nif (!(await Omnichannel.isWithinMACLimit(room))) { /* hard fail */ }\n\n// after - surface a meaningful message to the operator and short-circuit before the transcript flow\nif (!(await Omnichannel.isWithinMACLimit(room))) {\n  throw new Meteor.Error('error-mac-limit-reached', 'Monthly Active Contacts limit reached for this license tier.');\n}","handlingStrategy":"validation","validationCode":"// Before requesting a transcript, check the MAC posture\nconst macOk = await fetch('/api/v1/v1/livechat/room/' + rid + '/transcript', { method: 'GET' });\n// or proactively confirm license/EE status and visitor.activity month\nconst activityMonth = room.v?.activity; // expects YYYY-MM substring\nconst currentMonth = new Date().toISOString().slice(0,7);\nif (!activityMonth?.includes(currentMonth)) {\n  // contact will be counted as new this month; verify MAC headroom with admin\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Monitor monthlyActiveContacts usage in the admin license dashboard and alert before the cap.","Track each visitor's v.activity month client-side to anticipate new-month contacts.","For EE deployments, size the license tier to expected monthly new contacts."],"tags":["omnichannel","license","ee","monthly-active-contacts","transcript"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}