RocketChat/Rocket.Chat · error · Error

error-invalid-room

error-invalid-room

Error message

error-invalid-room

What it means

Thrown by DELETE livechat/transcript/:rid when the room lookup returns null OR room.open is false. The endpoint cancels a previously-requested email transcript; cancellation only makes sense on an open conversation, so a missing or already-closed room is rejected before checking whether a transcript was requested.

Source

Thrown at apps/meteor/server/api/v1/omnichannel/transcript.ts:42

API.v1.addRoute(
	'livechat/transcript/:rid',
	{
		authRequired: true,
		permissionsRequired: ['send-omnichannel-chat-transcript'],
		validateParams: {
			POST: isPOSTLivechatTranscriptRequestParams,
		},
	},
	{
		async delete() {
			const { rid } = this.urlParams;
			const room = await LivechatRooms.findOneById<Pick<IOmnichannelRoom, 'open' | 'transcriptRequest' | 'v'>>(rid, {
				projection: { open: 1, transcriptRequest: 1, v: 1 },
			});

			if (!room?.open) {
				throw new Error('error-invalid-room');
			}
			if (!room.transcriptRequest) {
				throw new Error('error-transcript-not-requested');
			}

			if (!(await Omnichannel.isWithinMACLimit(room))) {
				throw new Error('error-mac-limit-reached');
			}

			await LivechatRooms.unsetEmailTranscriptRequestedByRoomId(rid);

			return API.v1.success();
		},
		async post() {
			const { rid } = this.urlParams;
			const { email, subject } = this.bodyParams;

			const user = await Users.findOneById(this.userId, {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Check room.open before offering the cancel-transcript action in the UI.
  2. If the room closed naturally, the transcript request is moot — no cancel needed; inform the user.
  3. Refresh the room state before issuing DELETE if the action has been pending in the UI.
  4. Handle this error gracefully as 'no action needed' rather than an error toast.

Example fix

// before
await DELETE('/api/v1/livechat/transcript/' + rid);

// after
const room = await fetchRoom(rid);
if (!room?.open) { notify('Room closed - transcript request cleared on close'); return; }
await DELETE('/api/v1/livechat/transcript/' + rid);
Defensive patterns

Strategy: validation

Validate before calling

const room = await LivechatRooms.findOneById(rid, { projection: { open: 1, transcriptRequest: 1 } });
if (!room?.open) throw new ClientError('room-closed-or-missing');

Type guard

const isOpenForTranscript = (r: { open?: boolean; transcriptRequest?: unknown } | null): r is { open: true; transcriptRequest: unknown } =>
  !!r && r.open === true;

Try / catch

try {
  await DELETE('/api/v1/livechat/transcript/' + rid);
} catch (e) {
  if (e.message === 'error-invalid-room') { notify('Room is closed - nothing to cancel'); return; }
  throw e;
}

Prevention

When it happens

Trigger: DELETE /api/v1/livechat/transcript/<rid> against a room id that does not exist, or whose open flag is false. The projection only fetches open, transcriptRequest, v, so a closed room reaches this guard.

Common situations: User clicks 'cancel transcript' on a room that was just closed by the agent/inactivity; client holds a stale rid; race between close and the cancel click; transcript request was on a room that has since been deleted.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/0924bbc83fea6167. Report an issue: GitHub.