{"record":{"id":"41f8b0e563394b40","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-41f8b0","errorCode":null,"errorMessage":"error-not-allowed","messagePattern":"error-not-allowed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/lib/visitors.ts","lineNumber":62,"sourceCode":"}\n\nexport async function findChatHistory({\n\tuserId,\n\troomId,\n\tvisitorId,\n\tpagination: { offset, count, sort },\n}: {\n\tuserId: IUser['_id'];\n\troomId: IRoom['_id'];\n\tvisitorId: IVisitor['_id'];\n\tpagination: { offset: number; count: number; sort: FindOptions<IOmnichannelRoom>['sort'] };\n}) {\n\tconst room = await LivechatRooms.findOneById(roomId);\n\tif (!room) {\n\t\tthrow new Error('invalid-room');\n\t}\n\tif (!(await canAccessRoomAsync(room, { _id: userId }))) {\n\t\tthrow new Error('error-not-allowed');\n\t}\n\n\tconst extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}, { userId });\n\tconst { cursor, totalCount } = LivechatRooms.findPaginatedByVisitorId(\n\t\tvisitorId,\n\t\t{\n\t\t\tsort: sort || { ts: -1 },\n\t\t\tskip: offset,\n\t\t\tlimit: count,\n\t\t},\n\t\textraQuery,\n\t);\n\n\tconst [history, total] = await Promise.all([cursor.toArray(), totalCount]);\n\n\treturn {\n\t\thistory,\n\t\tcount: history.length,","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/omnichannel/lib/visitors.ts#L44-L80","documentation":"Thrown by findChatHistory (visitors.ts:61-63) when the room exists but canAccessRoomAsync(room, { _id: userId }) returns false — the calling user is not permitted in that livechat room (not the assigned agent, not a participant, or lacking the relevant room/omnichannel permissions). Returns HTTP 400 { success:false, error:'error-not-allowed' }.","triggerScenarios":"A user without access rights to the specific room calls findChatHistory (e.g. an agent not serving the room, or a user lacking view-l-room).","commonSituations":"Agent not assigned to the room tries to read its history; user lacks `view-l-room`; cross-department/cross-tenant access attempt.","solutions":["Grant the user access to the room (assign them as agent, or grant `view-l-room`).","Verify the user is the room's servedBy agent or has manager/moderator rights before calling.","Use a user context that already has access (e.g. an admin/manager)."],"exampleFix":"// before\nfindChatHistory({ userId, roomId, visitorId, pagination }); // throws error-not-allowed\n\n// after\nif (!(await canAccessRoomAsync(room, { _id: userId }))) return forbidden();\nfindChatHistory({ userId, roomId, visitorId, pagination });","handlingStrategy":"validation","validationCode":"const room = await LivechatRooms.findOneById(roomId);\nif (!room) throw new Error('invalid-room');\nif (!(await canAccessRoomAsync(room, { _id: userId }))) {\n  throw new Error('user cannot access this room');\n}\n// safe to call findChatHistory","typeGuard":"const canAccessRoom = async (room: IOmnichannelRoom, userId: string) =>\n  await canAccessRoomAsync(room, { _id: userId });","tryCatchPattern":"try { await findChatHistory({ userId, roomId, visitorId, pagination }); }\ncatch (e) { if (e instanceof Error && e.message === 'error-not-allowed') { /* forbidden: needs access/role */ } else throw e; }","preventionTips":["Run canAccessRoomAsync before reading a room's history.","Grant `view-l-room` or assign the agent to the room for legitimate readers.","Don't expose history actions to users outside the room."],"tags":["livechat","omnichannel","permission","room"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}