RocketChat/Rocket.Chat · error · Error
error-invalid-room
error-invalid-room
Error message
error-invalid-room
What it means
POST livechat/room.onHold looks the room up by body roomId with LivechatRooms.findOneById; if no room matches, error-invalid-room is thrown before the subscription/permission check. The route itself requires the on-hold-livechat-room permission and an Enterprise license, so callers without those never reach this throw.
Source
Thrown at apps/meteor/ee/server/api/v1/omnichannel/rooms.ts:29
API.v1.addRoute(
'livechat/room.onHold',
{
authRequired: true,
permissionsRequired: ['on-hold-livechat-room'],
validateParams: isLivechatRoomOnHoldProps,
license: ['livechat-enterprise'],
},
{
async post() {
const { roomId } = this.bodyParams;
type Room = Pick<IOmnichannelRoom, '_id' | 't' | 'open' | 'onHold' | 'u' | 'lastMessage' | 'servedBy'>;
const room = await LivechatRooms.findOneById<Room>(roomId, {
projection: { _id: 1, t: 1, open: 1, onHold: 1, u: 1, lastMessage: 1, servedBy: 1 },
});
if (!room) {
throw new Error('error-invalid-room');
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, { projection: { _id: 1 } });
if (!subscription && !(await hasPermissionAsync(this.user, 'on-hold-others-livechat-room'))) {
throw new Error('Not_authorized');
}
const onHoldBy = { _id: this.userId, username: this.user.username, name: this.user.name };
const comment = i18n.t('Omnichannel_On_Hold_manually', {
user: onHoldBy.name || `@${onHoldBy.username}`,
});
await OmnichannelEEService.placeRoomOnHold(room, comment, this.user);
return API.v1.success();
},
},
);View on GitHub (pinned to b2c16d5842)
Solutions
- Fetch the room fresh (livechat rooms list / rooms.info) and confirm it is an open omnichannel conversation before calling.
- Verify the room type is 'l' — on-hold only applies to livechat rooms.
- Check workspace/environment when ids come from external configuration.
Example fix
// before
await api.post('/v1/livechat/room.onHold', { roomId: rid });
// after
const info = await api.get('/v1/rooms.info', { params: { roomId: rid } });
if (info?.room?.t === 'l' && info.room.open) {
await api.post('/v1/livechat/room.onHold', { roomId: rid });
} Defensive patterns
Strategy: validation
Validate before calling
const info = await api.get('/v1/rooms.info', { params: { roomId } });
if (info?.room?.t === 'l' && info.room.open && !info.room.onHold) {
await api.post('/v1/livechat/room.onHold', { roomId });
} Try / catch
try {
await api.post('/v1/livechat/room.onHold', { roomId });
} catch (e) {
if (e?.response?.data?.errorType === 'error-invalid-room') {
// drop the room from the queue; it no longer exists
} else throw e;
} Prevention
- Confirm the room is an open livechat conversation before offering the hold action.
- Refresh room existence for ids older than the current session.
- Keep room ids environment-scoped in configuration.
When it happens
Trigger: POST /api/v1/livechat/room.onHold with {"roomId": "<unknown id>"} — typo, deleted room, non-livechat room id, or an id from another workspace.
Common situations: Automation holding rooms after they closed; passing a channel/group id for a non-omnichannel room; environment mismatch between where the id was read and where the call is made.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- error-room-does-not-exist
- invalid-room
- error-invalid-room
- error-contact-not-found
- error-visitor-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/7442d6236e4ce230.
Report an issue: GitHub.