RocketChat/Rocket.Chat · error · Meteor.Error
cannot-access-room
cannot-access-room
Error message
cannot-access-room
What it means
When the found room is open, getRoom compares room.v.token with guest.token. A mismatch means the caller is trying to use a room that belongs to a different visitor, so Meteor.Error('cannot-access-room') is thrown instead of returning the room.
Source
Thrown at apps/meteor/server/lib/omnichannel/rooms.ts:80
if (room?.v._id && (await LivechatContacts.isChannelBlocked(Visitors.makeVisitorAssociation(room.v._id, room.source)))) {
throw new Error('error-contact-channel-blocked');
}
if (!room?.open) {
livechatLogger.debug({ msg: 'Last room for visitor closed. Creating new one', visitorId: guest._id });
}
if (!room?.open) {
return {
room: await createRoom({ visitor: guest, message: message.msg, roomInfo, agent, extraData }),
newRoom: true,
};
}
if (room.v.token !== guest.token) {
livechatLogger.debug({ msg: 'Visitor trying to access another visitor room', visitorId: guest._id });
throw new Meteor.Error('cannot-access-room');
}
return { room, newRoom: false };
}
export async function createRoom({
visitor,
message,
rid,
roomInfo,
agent,
extraData,
}: {
visitor: ILivechatVisitor;
message?: string;
rid?: string;
roomInfo: IOmnichannelRoomInfo;
agent?: SelectedAgent;View on GitHub (pinned to b2c16d5842)
Solutions
- Always obtain a room via the visitor's own token instead of reusing a stored rid.
- Clear the stored rid and token together whenever the visitor session resets.
- In REST integrations, fetch rid from the same visitor context that owns the token (livechat visitor/room creation endpoints).
Example fix
// before
// reusing rid from another session -> cannot-access-room
const { room } = await getRoom(guest, { rid: storedRid, msg, token }, roomInfo);
// after
// drop the stale rid; let the flow find/create the room for this token
const { room, newRoom } = await getRoom(guest, { rid: makeRid(guest.token), msg, token: guest.token }, roomInfo); Defensive patterns
Strategy: validation
Validate before calling
const room = await LivechatRooms.findOneById(message.rid, { projection: { 'v.token': 1, open: 1 } });
if (room?.open && room.v?.token !== guest.token) {
throw new Error('rid belongs to a different visitor - request a new room');
} Type guard
const isRoomOwnedByGuest = (room: { v?: { token?: string } } | null, guest: { token?: string }): boolean =>
Boolean(room?.v?.token && room.v.token === guest.token); Try / catch
try {
const { room } = await getRoom(guest, message, roomInfo);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'cannot-access-room') {
// drop the stored rid and create a fresh room for this token
return;
}
throw err;
} Prevention
- Never persist rid beyond the visitor session that produced it.
- Reset rid and token together when the visitor is re-initialized.
- Derive rooms from the visitor's own token via the official livechat endpoints.
When it happens
Trigger: Calling the livechat flow with a rid whose room belongs to another visitor token: an rid copied from another session, a token that was regenerated (visitor record recreated / cookies cleared) while a stale rid is reused, or two widget instances sharing state.
Common situations: Persisted rid in localStorage surviving a visitor re-initialization; test scripts reusing rid/token pairs; multi-tab widgets leaking token/rid state.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/9c98bffee26e02b9.
Report an issue: GitHub.