RocketChat/Rocket.Chat · error · Error

error-invalid-room

error-invalid-room

Error message

error-invalid-room

What it means

POST omnichannel/:rid/request-transcript loads the livechat room from the URL param with LivechatRooms.findOneById; a miss throws error-invalid-room. The route requires the request-pdf-transcript permission and an Enterprise license, and only projects _id/open/v/t/pdfTranscriptFileId for the transcript flow.

Source

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

import { LivechatRooms } from '@rocket.chat/models';

import { API } from '../../../../../server/api';
import { canAccessRoomAsync } from '../../../../../server/lib/authorization/canAccessRoom';
import { requestPdfTranscript } from '../../../lib/omnichannel/requestPdfTranscript';

API.v1.addRoute(
	'omnichannel/:rid/request-transcript',
	{ authRequired: true, permissionsRequired: ['request-pdf-transcript'], license: ['livechat-enterprise'] },
	{
		async post() {
			const room = await LivechatRooms.findOneById<Pick<IOmnichannelRoom, '_id' | 'open' | 'v' | 't' | 'pdfTranscriptFileId'>>(
				this.urlParams.rid,
				{
					projection: { _id: 1, open: 1, v: 1, t: 1, pdfTranscriptFileId: 1 },
				},
			);
			if (!room) {
				throw new Error('error-invalid-room');
			}

			if (!(await canAccessRoomAsync(room, { _id: this.userId }))) {
				throw new Error('error-not-allowed');
			}

			// Flow is as follows:
			// 1. On Test Mode, call Transcript.workOnPdf directly
			// 2. On Normal Mode, call QueueWorker.queueWork to queue the work
			// 3. OmnichannelTranscript.workOnPdf will be called by the worker to generate the transcript
			// 4. We be happy :)
			await requestPdfTranscript(room, this.userId);

			return API.v1.success();
		},
	},
);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Use the exact room _id in the URL path (GET the room first if unsure) and URL-encode it.
  2. Confirm the room is an omnichannel (type l) room — the endpoint is omnichannel-only.
  3. Gate transcript actions in the UI on a fresh room existence check, not cached state.

Example fix

// before
await api.post(`/v1/omnichannel/${rid}/request-transcript`);

// after
const info = await api.get('/v1/rooms.info', { params: { roomId: rid } });
if (info?.room?.t === 'l') {
  await api.post(`/v1/omnichannel/${encodeURIComponent(rid)}/request-transcript`);
}
Defensive patterns

Strategy: validation

Validate before calling

const info = await api.get('/v1/rooms.info', { params: { roomId: rid } });
if (info?.room?.t === 'l') {
  await api.post(`/v1/omnichannel/${encodeURIComponent(rid)}/request-transcript`);
}

Try / catch

try {
  await api.post(`/v1/omnichannel/${encodeURIComponent(rid)}/request-transcript`);
} catch (e) {
  if (e?.response?.data?.errorType === 'error-invalid-room') {
    // rid is wrong or the room is gone: re-resolve the room id
  } else throw e;
}

Prevention

When it happens

Trigger: POST /api/v1/omnichannel/<bogus-rid>/request-transcript — rid typo or truncation, deleted/purged room, a room name instead of id, or a non-omnichannel room id.

Common situations: URL-building code that drops or mangles the :rid segment; requesting transcripts for rooms removed by retention; ids copied from another environment.

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/0d3657e4510c4310. Report an issue: GitHub.