{"record":{"id":"3354c741d8328bb9","repo":"RocketChat/Rocket.Chat","slug":"last-owner-can-not-be-removed","errorCode":null,"errorMessage":"last-owner-can-not-be-removed","messagePattern":"last-owner-can-not-be-removed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/services/team/service.ts","lineNumber":804,"sourceCode":"\t\tconst byUser = await Users.findOneById(uid);\n\n\t\tfor await (const member of members) {\n\t\t\tif (!member.userId) {\n\t\t\t\tthrow new Error('invalid-user');\n\t\t\t}\n\n\t\t\tconst existingMember = await TeamMember.findOneByUserIdAndTeamId(member.userId, team._id);\n\t\t\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(team.roomId, member.userId);\n\n\t\t\tif (!existingMember && !subscription) {\n\t\t\t\tthrow new Error('member-does-not-exist');\n\t\t\t}\n\n\t\t\tif (existingMember) {\n\t\t\t\tif (existingMember.roles?.includes('owner')) {\n\t\t\t\t\tconst totalOwners = await TeamMember.countByTeamIdAndRole(team._id, 'owner');\n\t\t\t\t\tif (totalOwners === 1) {\n\t\t\t\t\t\tthrow new Error('last-owner-can-not-be-removed');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait TeamMember.removeById(existingMember._id);\n\t\t\t}\n\n\t\t\tconst removedUser = usersToRemove.find((u) => u._id === (existingMember || member).userId);\n\t\t\tif (removedUser) {\n\t\t\t\tawait removeUserFromRoom(\n\t\t\t\t\tteam.roomId,\n\t\t\t\t\tremovedUser,\n\t\t\t\t\tuid !== member.userId && byUser\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tbyUser,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t);\n\t\t\t}","sourceCodeStart":786,"sourceCodeEnd":822,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/e4b8178b205510181a96ceefee043d0abcd13e5a/apps/meteor/server/services/team/service.ts#L786-L822","documentation":"TeamService.removeMembers refuses to delete a team's last owner: before removing an owner it counts owners via TeamMember.countByTeamIdAndRole(team._id, 'owner') and throws when exactly one remains. The guard guarantees every team keeps at least one member with the 'owner' role able to manage members and settings. It fires per-member inside the loop, after the member-existence checks.","triggerScenarios":"POST teams.removeMember (or teams.leave by the owner) targeting the sole owner of a team; a bulk removeMembers batch that includes the only owner; a single-member team whose creator tries to leave without transferring ownership.","commonSituations":"Solo teams where the creator is the only owner; cleanup scripts that remove all members in one batch; owner using teams.leave without first promoting someone else.","solutions":["Promote another member to owner first via Team.addRolesToMember(teamId, successorId, ['owner']), then remove the old owner","If the team should cease to exist, delete the team (teams.delete) instead of removing its last owner","Before removal, count owners with TeamMember.countByTeamIdAndRole(teamId, 'owner') and block the action in the UI when it equals 1","For teams.leave, transfer ownership before leaving"],"exampleFix":"// before\nawait Team.removeMembers(uid, teamId, [{ userId: lastOwnerId }]);\n\n// after\nconst owners = await TeamMember.countByTeamIdAndRole(teamId, 'owner');\nif (owners === 1) {\n  await Team.addRolesToMember(teamId, successorId, ['owner']);\n}\nawait Team.removeMembers(uid, teamId, [{ userId: lastOwnerId }]);","handlingStrategy":"validation","validationCode":"const member = await TeamMember.findOneByUserIdAndTeamId(userId, teamId);\nif (member?.roles?.includes('owner')) {\n  const totalOwners = await TeamMember.countByTeamIdAndRole(teamId, 'owner');\n  if (totalOwners === 1) {\n    throw new Error('promote-another-owner-first');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await Team.removeMembers(uid, teamId, members);\n} catch (err) {\n  if (err instanceof Error && err.message === 'last-owner-can-not-be-removed') {\n    // surface 'transfer ownership first' to the user\n  }\n  throw err;\n}","preventionTips":["Disable the Remove/Leave action for the last owner in the UI","Run ownership transfer before owner removal in scripts","Keep at least two owners on production teams"],"tags":["teams","ownership","business-rule"],"backgroundTag":"cannot-remove-last-owner","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"}