RocketChat/Rocket.Chat · error · Error

type-and-room-not-compatible

Error message

type-and-room-not-compatible

What it means

Thrown by VideoConferenceService.create when type === 'direct' but the room cannot support direct ringing — isRoomCompatibleWithVideoConfRinging(room.t, room.uids) fails. Direct (ringing) calls require a direct-message room with at most the two call participants; passing type 'direct' with a channel, private group, team, or livechat room id hits this guard.

Source

Thrown at apps/meteor/server/services/video-conference/service.ts:92

		useAppUser = true,
	): Promise<VideoConferenceInstructions> {
		return wrapExceptions(async () => {
			const room = await Rooms.findOneById<Pick<IRoom, '_id' | 't' | 'uids' | 'name' | 'fname'>>(rid, {
				projection: { t: 1, uids: 1, name: 1, fname: 1 },
			});

			if (!room) {
				throw new Error('invalid-room');
			}

			const user = await Users.findOneById<IUser>(createdBy);
			if (!user) {
				throw new Error('failed-to-load-own-data');
			}

			if (type === 'direct') {
				if (!isRoomCompatibleWithVideoConfRinging(room.t, room.uids)) {
					throw new Error('type-and-room-not-compatible');
				}

				return this.startDirect(providerName, user, room, data);
			}

			if (type === 'livechat') {
				return this.startLivechat(providerName, user, rid);
			}

			const title = (data as Partial<IGroupVideoConference>).title || room.fname || room.name || '';
			return this.startGroup(providerName, user, room._id, title, data, useAppUser);
		}).catch((err) => {
			logger.error({
				name: 'Error on VideoConf.create',
				err,
			});
			throw err;
		});

View on GitHub (pinned to e4b8178b20)

Solutions

  1. Send the type that matches the room: 'direct' only for 1:1 DM rooms; omit/group type for channels and teams
  2. Derive the type from the room record instead of hardcoding it (see exampleFix)
  3. Hide the ringing call button outside 1:1 DMs
  4. Validate the (type, rid) pair at the API boundary with a schema

Example fix

// before
await VideoConfService.create({ type: 'direct', rid, createdBy, providerName }); // rid is a channel

// after
const room = await Rooms.findOneById(rid, { projection: { t: 1, uids: 1 } });
const type = room?.t === 'd' && (room.uids?.length ?? 0) <= 2 ? 'direct' : undefined;
await VideoConfService.create({ type, rid, createdBy, providerName });
Defensive patterns

Strategy: type-guard

Validate before calling

const room = await Rooms.findOneById(rid, { projection: { t: 1, uids: 1 } });
if (type === 'direct' && !(room?.t === 'd' && (room.uids?.length ?? 0) <= 2)) {
  throw new Meteor.Error('type-and-room-not-compatible');
}

Type guard

function isRoomCompatibleWithDirectRinging(room: Pick<IRoom, 't' | 'uids'>): boolean {
  return room.t === 'd' && (room.uids?.length ?? 0) <= 2;
}

Try / catch

try {
  await VideoConfService.create({ type: 'direct', rid, createdBy, providerName });
} catch (err) {
  if (err instanceof Error && err.message === 'type-and-room-not-compatible') {
    // fall back to a non-ringing group call for this room
  }
  throw err;
}

Prevention

When it happens

Trigger: POST video-conference/create with { type: 'direct', rid: <channel id> }; a front-end hardcodes type 'direct' for every room; a DM grew beyond two uids after multi-user direct changes.

Common situations: One call-start code path reused for all room types with a fixed type string; UI state desync between the open room and the type flag; group-DM edge cases.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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