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
- Send the type that matches the room: 'direct' only for 1:1 DM rooms; omit/group type for channels and teams
- Derive the type from the room record instead of hardcoding it (see exampleFix)
- Hide the ringing call button outside 1:1 DMs
- 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
- Compute the call type from the room record; never hardcode it
- Show ringing UI only in 1:1 DMs
- Tie type to room shape in endpoint payload validation
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
- A video conference must exist to update.
- room_is_blocked
- error-invalid-room
- invalid call
- error-invalid-setting-value
AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18).
Data as JSON: /api/errors/da4ba49ffdb7cfe5.
Report an issue: GitHub.