{"record":{"id":"ff404111b4cb5269","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-team-no-main-room","errorCode":null,"errorMessage":"error-invalid-team-no-main-room","messagePattern":"error-invalid-team-no-main-room","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/services/team/service.ts","lineNumber":1091,"sourceCode":"\n\t\tconst parentRoom = await this.getParentRoom(team);\n\t\treturn { team, ...(parentRoom && { parentRoom }) };\n\t}\n\n\t// Returns the list of rooms and discussions a user has access to inside a team\n\t// Rooms returned are a composition of the rooms the user is in + public rooms + discussions from the main room (if any)\n\tasync listChildren(\n\t\tuserId: string,\n\t\tteam: AtLeast<ITeam, '_id' | 'roomId' | 'type'>,\n\t\tfilter?: string,\n\t\ttype?: 'channels' | 'discussions',\n\t\tsort?: Record<string, 1 | -1>,\n\t\tskip = 0,\n\t\tlimit = 10,\n\t): Promise<{ total: number; data: IRoom[] }> {\n\t\tconst mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });\n\t\tif (!mainRoom) {\n\t\t\tthrow new Error('error-invalid-team-no-main-room');\n\t\t}\n\n\t\tconst isMember = await TeamMember.findOneByUserIdAndTeamId(userId, team._id, {\n\t\t\tprojection: { _id: 1 },\n\t\t});\n\n\t\tif (!isMember) {\n\t\t\tthrow new Error('error-invalid-team-not-a-member');\n\t\t}\n\n\t\tconst [{ totalCount: [{ count: total }] = [], paginatedResults: data = [] }] =\n\t\t\t(await Rooms.findChildrenOfTeam(team._id, mainRoom._id, userId, filter, type, { skip, limit, sort }).toArray()) || [];\n\n\t\treturn {\n\t\t\ttotal,\n\t\t\tdata,\n\t\t};\n\t}","sourceCodeStart":1073,"sourceCodeEnd":1109,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/e4b8178b205510181a96ceefee043d0abcd13e5a/apps/meteor/server/services/team/service.ts#L1073-L1109","documentation":"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.","triggerScenarios":"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.","commonSituations":"Manual MongoDB surgery removed the main channel; a backup restore included teams but not rooms; import scripts copying teams without their rooms.","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"],"exampleFix":"// before\nconst { total, data } = await Teams.listChildren(userId, team);\n\n// after\nconst mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });\nif (!mainRoom) {\n  // data integrity issue — repair team record or skip listing\n  return { total: 0, data: [] };\n}\nconst { total, data } = await Teams.listChildren(userId, team);","handlingStrategy":"validation","validationCode":"const mainRoom = await Rooms.findOneById(team.roomId, { projection: { _id: 1 } });\nif (!mainRoom) {\n  throw new Error('error-invalid-team-no-main-room'); // surface a repair hint instead of a raw crash\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await Teams.listChildren(userId, team);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-invalid-team-no-main-room') {\n    logger.error({ msg: 'orphaned team', teamId: team._id });\n    return { total: 0, data: [] };\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["teams","data-integrity","rooms"],"backgroundTag":"orphaned-database-reference","analyzedSha":"e4b8178b205510181a96ceefee043d0abcd13e5a","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}