RocketChat/Rocket.Chat · error · Error

error-invalid-team-no-main-room

Error message

error-invalid-team-no-main-room

What it means

Thrown by TeamService.listChildren when the team's main room (team.roomId) no longer exists — Rooms.findOneById returns null. Every team is expected to own a main channel; a missing one means broken referential integrity between the Team and Rooms collections, not a bad request. The check runs before the membership check, so it fires for valid members too.

Source

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

		const parentRoom = await this.getParentRoom(team);
		return { team, ...(parentRoom && { parentRoom }) };
	}

	// Returns the list of rooms and discussions a user has access to inside a team
	// Rooms returned are a composition of the rooms the user is in + public rooms + discussions from the main room (if any)
	async listChildren(
		userId: string,
		team: AtLeast<ITeam, '_id' | 'roomId' | 'type'>,
		filter?: string,
		type?: 'channels' | 'discussions',
		sort?: Record<string, 1 | -1>,
		skip = 0,
		limit = 10,
	): Promise<{ total: number; data: IRoom[] }> {
		const mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });
		if (!mainRoom) {
			throw new Error('error-invalid-team-no-main-room');
		}

		const isMember = await TeamMember.findOneByUserIdAndTeamId(userId, team._id, {
			projection: { _id: 1 },
		});

		if (!isMember) {
			throw new Error('error-invalid-team-not-a-member');
		}

		const [{ totalCount: [{ count: total }] = [], paginatedResults: data = [] }] =
			(await Rooms.findChildrenOfTeam(team._id, mainRoom._id, userId, filter, type, { skip, limit, sort }).toArray()) || [];

		return {
			total,
			data,
		};
	}

View on GitHub (pinned to e4b8178b20)

Solutions

  1. Repair the data: recreate the team's main room or remove the orphaned team document so listings stop hitting it
  2. Audit teams periodically with Rooms.findOneById(team.roomId) and fix mismatches
  3. Delete teams only via official APIs (teams.delete) which clean up both sides
  4. Catch the error and hide unrepaired teams from directory listings

Example fix

// before
const { total, data } = await Teams.listChildren(userId, team);

// after
const mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });
if (!mainRoom) {
  // data integrity issue — repair team record or skip listing
  return { total: 0, data: [] };
}
const { total, data } = await Teams.listChildren(userId, team);
Defensive patterns

Strategy: validation

Validate before calling

const mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });
if (!mainRoom) {
  throw new Error('error-invalid-team-no-main-room'); // surface a repair hint instead of a raw crash
}

Try / catch

try {
  return await Teams.listChildren(userId, team);
} catch (err) {
  if (err instanceof Error && err.message === 'error-invalid-team-no-main-room') {
    logger.error({ msg: 'orphaned team', teamId: team._id });
    return { total: 0, data: [] };
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling listChildren (teams.listRooms / channels / discussions flows) for a team whose main channel was deleted directly in the database or lost in a partial migration/restore; a team document orphaned by a failed team deletion.

Common situations: Manual MongoDB surgery removed the main channel; a backup restore included teams but not rooms; import scripts copying teams without their rooms.

Related errors


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