RocketChat/Rocket.Chat · error · Error

Room id not found

Error message

Room id not found

What it means

Thrown by AppRoomBridge.update when Rooms.updateOne({_id: room.id}, {$set: rm}) reports matchedCount === 0. The bridge converts the App's IRoom to a raw room, runs the update, and treats zero matches as 'the room does not exist'. Member additions are skipped because the throw precedes the members loop.

Source

Thrown at apps/meteor/app/apps/server/bridges/rooms.ts:239

	protected async getDirectByUsernames(usernames: Array<string>, appId: string): Promise<IRoom | undefined> {
		this.orch.debugLog(`The App ${appId} is getting direct room by usernames: "${usernames}"`);
		const room = await Rooms.findDirectRoomContainingAllUsernames(usernames, {});
		if (!room) {
			return undefined;
		}
		return this.orch.getConverters()?.get('rooms').convertRoom(room);
	}

	protected async update(room: IRoom, members: Array<string> = [], appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is updating a room.`);

		const rm = await this.orch.getConverters()?.get('rooms').convertAppRoom(room, true);

		const updateResult = await Rooms.updateOne({ _id: room.id }, { $set: rm });

		if (!updateResult.matchedCount) {
			throw new Error('Room id not found');
		}

		for (const username of members) {
			const member = await Users.findOneByUsername(username, {});

			if (!member) {
				continue;
			}

			await addUserToRoom(room.id, member);
		}
	}

	protected async delete(roomId: string, appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is deleting a room.`);
		await deleteRoom(roomId);
	}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Verify the room exists (getById) before calling update, and surface a clear error to the user if not.
  2. Refresh the IRoom from the accessor before updating so room.id is current.
  3. If the room may have been deleted, treat matchedCount 0 as a recoverable case in your App's logic rather than propagating the bridge error.

Example fix

// before
await rooms.update(staleRoom, members, appId);

// after
const existing = await rooms.getById(staleRoom.id, appId);
if (!existing) throw new Error(`Room ${staleRoom.id} no longer exists`);
await rooms.update(staleRoom, members, appId);
Defensive patterns

Strategy: validation

Validate before calling

const existing = await rooms.getById(room.id, appId);
if (!existing) {
  throw new Error(`Cannot update: room ${room.id} does not exist`);
}
await rooms.update(room, members, appId);

Try / catch

try {
  await rooms.update(room, members, appId);
} catch (e) {
  if (e instanceof Error && /Room id not found/.test(e.message)) {
    this.app.getLogger().warn({ msg: 'Room disappeared before update', roomId: room.id });
    return; // or recreate / re-fetch
  }
  throw e;
}

Prevention

When it happens

Trigger: An App calls the rooms accessor's update with a room whose id does not correspond to any persisted room document — a deleted room, a fabricated id, a room on another workspace, or an id extracted from a stale/federated reference that has no local document.

Common situations: Updating a room from a cached IRoom after it was deleted; federation or cross-workspace ids; acting on a webhook for a room that was archived; passing room.id from an object whose id field was never populated.

Related errors


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