RocketChat/Rocket.Chat · error · Error

room-name-already-exists

Error message

room-name-already-exists

What it means

After the name-availability check, TeamService.create() also runs Rooms.findOneByName(team.name); if a room with that name already exists under a different id than the provided room.id, it throws room-name-already-exists — the team's channel would collide with an existing room name.

Source

Thrown at apps/meteor/server/services/team/service.ts:49

import { addUserToRoom } from '../../lib/rooms/addUserToRoom';
import { getSubscribedRoomsForUserWithDetails } from '../../lib/rooms/getRoomsWithSingleOwner';
import { removeUserFromRoom } from '../../lib/rooms/removeUserFromRoom';
import { saveRoomName } from '../../lib/rooms/settings';
import { saveRoomType } from '../../lib/rooms/settings/saveRoomType';
import { checkUsernameAvailability } from '../../lib/users/checkUsernameAvailability';
import { settings } from '../../settings';

export class TeamService extends ServiceClassInternal implements ITeamService {
	protected name = 'team';

	async create(uid: string, { team, room = { name: team.name, extraData: {} }, members, owner }: ITeamCreateParams): Promise<ITeam> {
		if (!(await checkUsernameAvailability(team.name, 'room'))) {
			throw new Error('team-name-already-exists');
		}

		const existingRoom = await Rooms.findOneByName(team.name, { projection: { _id: 1 } });
		if (existingRoom && existingRoom._id !== room.id) {
			throw new Error('room-name-already-exists');
		}

		const createdBy = await Users.findOneById<Pick<IUser, 'username' | '_id'>>(uid, {
			projection: { username: 1 },
		});
		if (!createdBy) {
			throw new Error('invalid-user');
		}

		// TODO add validations to `data` and `members`

		const membersResult =
			!members || !Array.isArray(members) || members.length === 0
				? []
				: await Users.findActiveByIdsOrUsernames(members, {
						projection: { username: 1 },
					}).toArray();
		const memberUsernames = membersResult.map(({ username }) => username);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Rename the team, or delete/rename the colliding room first
  2. If the existing room is intended to back the team, pass its id as room.id in the create params
  3. Pre-check Rooms.findOneByName(team.name) before submitting
Defensive patterns

Strategy: validation

Validate before calling

const existingRoom = await Rooms.findOneByName(team.name, { projections: { _id: 1 } });
if (existingRoom && existingRoom._id !== intendedRoomId) {
  // a room already uses this name: pick another team name or pass this room.id deliberately
  return markRoomNameTaken(team.name);
}

Type guard

const isRoomNameFreeForTeam = async (name: string, roomId?: string): Promise<boolean> => {
  const existing = await Rooms.findOneByName(name, { projections: { _id: 1 } });
  return !existing || existing._id === roomId;
};

Try / catch

try {
  await teamService.create(uid, params);
} catch (err) {
  if (err instanceof Error && err.message === 'room-name-already-exists') {
    // rename the team or delete/rename the colliding room; retrying the same name always fails
    return suggestAlternativeNames(params.team.name);
  }
  throw err;
}

Prevention

When it happens

Trigger: Creating a team when a channel/private room with the same name already exists (and the caller did not pass that exact room.id); races where two creates pick the same name at once.

Common situations: A pre-existing channel shares the desired team name; a deleted team left its channel behind; users re-running a failed create whose channel was actually created.

Related errors


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