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
- Repair the data: recreate the team's main room or remove the orphaned team document so listings stop hitting it
- Audit teams periodically with Rooms.findOneById(team.roomId) and fix mismatches
- Delete teams only via official APIs (teams.delete) which clean up both sides
- 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
- Delete teams only through official APIs so both documents are cleaned
- Add a team.roomId consistency check to maintenance scripts
- Quarantine orphaned teams from listings instead of letting them break the directory
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
- error-room-not-found
- error-invalid-user
- error-room-does-not-exist
- error-cannot-delete-team-channel
- room-name-already-exists
AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18).
Data as JSON: /api/errors/ff404111b4cb5269.
Report an issue: GitHub.