{"record":{"id":"fea14a55face7ae0","repo":"RocketChat/Rocket.Chat","slug":"room-not-on-team","errorCode":null,"errorMessage":"room-not-on-team","messagePattern":"room-not-on-team","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/services/team/service.ts","lineNumber":464,"sourceCode":"\n\t\tconst room = await Rooms.findOneById(rid);\n\t\tif (!room) {\n\t\t\tthrow new Error('invalid-room');\n\t\t}\n\n\t\tconst user = await Users.findOneById(uid);\n\t\tif (!user) {\n\t\t\tthrow new Error('invalid-user');\n\t\t}\n\t\tif (!canUpdateAnyRoom) {\n\t\t\tconst canSeeRoom = await Authorization.canAccessRoom(room, user);\n\t\t\tif (!canSeeRoom) {\n\t\t\t\tthrow new Error('invalid-room');\n\t\t\t}\n\t\t}\n\n\t\tif (!room.teamId) {\n\t\t\tthrow new Error('room-not-on-team');\n\t\t}\n\t\troom.teamDefault = isDefault;\n\t\tawait Rooms.setTeamDefaultById(rid, isDefault);\n\n\t\tif (isDefault) {\n\t\t\tconst maxNumberOfAutoJoinMembers = settings.get<number>('API_User_Limit');\n\t\t\tconst teamMembers = await this.members(\n\t\t\t\tuid,\n\t\t\t\troom.teamId,\n\t\t\t\ttrue,\n\t\t\t\t{ offset: 0, count: maxNumberOfAutoJoinMembers },\n\t\t\t\t// We should not get the owner of the room, since he is already a member\n\t\t\t\t{ _id: { $ne: room.u._id } },\n\t\t\t);\n\n\t\t\tfor await (const m of teamMembers.records) {\n\t\t\t\tif (await addUserToRoom(room._id, m.user, user)) {\n\t\t\t\t\troom.usersCount++;","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/services/team/service.ts#L446-L482","documentation":"Thrown by TeamService.updateRoom (apps/meteor/server/services/team/service.ts:464) when the fetched room document has no teamId field — the channel does not belong to any team. updateRoom only manages team channel properties (teamDefault / auto-join), so a non-team room is a state error rather than a not-found one. The room and user checks have already passed when this throws.","triggerScenarios":"POST /api/v1/teams.updateRoom for a channel that was already removed from its team (teams.removeRoom) or converted; calling updateRoom on a plain channel/private group that was never part of a team; a race where the room is detached from the team (unsetTeamId) between the endpoint's team lookup and the service call.","commonSituations":"Stale UI showing a channel under a team after it was removed elsewhere; scripts replaying old room lists; double-processing of the same request after the first already removed the room from the team.","solutions":["Confirm the room is still on the team before updating: check room.teamId equals your team's _id","Re-fetch the team's channel list (GET teams.listRooms) and use a current roomId","If the room legitimately left the team, drop the update — there is nothing to toggle; treat this error as an idempotency signal in retry logic"],"exampleFix":"// before\nawait Team.updateRoom(uid, rid, true); // throws room-not-on-team after removal\n\n// after\nconst room = await Rooms.findOneById(rid, { projection: { teamId: 1 } });\nif (!room?.teamId) {\n\treturn API.v1.failure('room-not-on-team'); // already detached — nothing to update\n}\nawait Team.updateRoom(uid, rid, true);","handlingStrategy":"validation","validationCode":"const room = await Rooms.findOneById(rid, { projection: { teamId: 1 } });\nif (!room?.teamId) {\n\tthrow new Error('room-not-on-team'); // nothing to update\n}\nawait Team.updateRoom(uid, rid, isDefault, canUpdateAny);","typeGuard":"const isTeamRoom = (room: IRoom | null): room is IRoom & { teamId: string } =>\n\t!!room && typeof room.teamId === 'string' && room.teamId.length > 0;","tryCatchPattern":"try {\n\tawait Team.updateRoom(uid, rid, isDefault);\n} catch (e) {\n\tif (e instanceof Error && e.message === 'room-not-on-team') {\n\t\t// room already detached — idempotent success or refresh UI\n\t}\n\tthrow e;\n}","preventionTips":["Re-fetch the team's channel list before editing after any removeRoom/convert operation","Treat room-not-on-team as an idempotency signal, not an error, in retry queues","Check room.teamId matches your expected team _id to catch cross-team staleness"],"tags":["rocket-chat","teams","rooms","invalid-state","validation"],"backgroundTag":"invalid-entity-state","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}