RocketChat/Rocket.Chat · warning · Error

member-does-not-exist

Error message

member-does-not-exist

What it means

Thrown by TeamService.removeMembers when, for a given member entry, neither a TeamMember document nor a subscription on the team's main room exists (TeamMember.findOneByUserIdAndTeamId and Subscriptions.findOneByRoomIdAndUserId both come back empty). The service checks both collections because a participant may exist only as a room subscriber; finding neither means the user is not on the team. The throw aborts the batch loop, so entries after the failing member are not processed.

Source

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

			throw new Error('team-does-not-exist');
		}

		const membersIds = members.map((m) => m.userId);
		const usersToRemove = await Users.findByIds(membersIds, {
			projection: { _id: 1, username: 1 },
		}).toArray();
		const byUser = await Users.findOneById(uid);

		for await (const member of members) {
			if (!member.userId) {
				throw new Error('invalid-user');
			}

			const existingMember = await TeamMember.findOneByUserIdAndTeamId(member.userId, team._id);
			const subscription = await Subscriptions.findOneByRoomIdAndUserId(team.roomId, member.userId);

			if (!existingMember && !subscription) {
				throw new Error('member-does-not-exist');
			}

			if (existingMember) {
				if (existingMember.roles?.includes('owner')) {
					const totalOwners = await TeamMember.countByTeamIdAndRole(team._id, 'owner');
					if (totalOwners === 1) {
						throw new Error('last-owner-can-not-be-removed');
					}
				}

				await TeamMember.removeById(existingMember._id);
			}

			const removedUser = usersToRemove.find((u) => u._id === (existingMember || member).userId);
			if (removedUser) {
				await removeUserFromRoom(
					team.roomId,
					removedUser,

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Check membership first with TeamMember.findOneByUserIdAndTeamId(userId, teamId) (and the main-room subscription) and treat absence as success
  2. Re-fetch the member list (GET teams.members) immediately before removing to avoid stale ids
  3. Make the removal flow idempotent: catch member-does-not-exist and return success since the goal state already holds
  4. Verify the userId exists in this workspace via Users.findOneById

Example fix

// before
await Team.removeMembers(uid, teamId, [{ userId }]);

// after
const existing = await TeamMember.findOneByUserIdAndTeamId(userId, teamId);
if (!existing) {
  // user already not on team — nothing to do
  return;
}
await Team.removeMembers(uid, teamId, [{ userId }]);
Defensive patterns

Strategy: validation

Validate before calling

const member = await TeamMember.findOneByUserIdAndTeamId(userId, teamId, { projection: { _id: 1 } });
const sub = member ? null : await Subscriptions.findOneByRoomIdAndUserId(team.roomId, userId);
if (!member && !sub) {
  // user is not on the team: nothing to remove
  return;
}

Try / catch

try {
  await Team.removeMembers(uid, teamId, [{ userId }]);
} catch (err) {
  if (err instanceof Error && err.message === 'member-does-not-exist') return; // idempotent no-op
  throw err;
}

Prevention

When it happens

Trigger: Calling Team.removeMembers / POST teams.removeMember with a userId that was already removed (double-submit, two admins removing at once), a user who left via teams.leave, or an id that never belonged to this workspace.

Common situations: Stale member list in the admin UI after someone else already removed the user; retry of a timed-out request that actually succeeded; userId copied from a different environment/database.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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