RocketChat/Rocket.Chat · warning · Meteor.Error

error-user-already-in-role

error-user-already-in-role

Error message

User already in role

What it means

Thrown by POST roles.addUserToRole when hasRoleAsync(user._id, role._id, roomId) is already true. The role lookup succeeded and the user was resolved via getUserFromParams, but the user already holds the role (optionally scoped to roomId). Returns a structured Meteor.Error. Note earlier failures in this action use return API.v1.failure(...) instead of throwing.

Source

Thrown at apps/meteor/server/api/v1/roles.ts:144

				400: validateBadRequestErrorResponse,
				401: validateUnauthorizedErrorResponse,
			},
		},
		async function action() {
			const user = await getUserFromParams(this.bodyParams);
			const { roleId, roomId } = this.bodyParams;

			if (!roleId) {
				return API.v1.failure('error-invalid-role-properties');
			}

			const role = await Roles.findOneById(roleId);
			if (!role) {
				return API.v1.failure('error-role-not-found', 'Role not found');
			}

			if (await hasRoleAsync(user._id, role._id, roomId)) {
				throw new Meteor.Error('error-user-already-in-role', 'User already in role');
			}

			await addUserToRole(this.userId, role._id, user.username, roomId);

			return API.v1.success({
				role,
			});
		},
	)
	.get(
		'roles.getUsersInRole',
		{
			authRequired: true,
			permissionsRequired: ['access-permissions'],
			query: isRolesGetUsersInRoleProps,
			response: {
				200: ajv.compile<{ users: IUserInRole[]; total: number }>({
					type: 'object',

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Treat the error as success for idempotent workflows (user is already in the desired state).
  2. Check membership first with GET /api/v1/roles.getUsersInRole before attempting the add.
  3. When scoping by roomId, verify whether the role is room-scoped before re-assigning.

Example fix

// before
await fetch('/api/v1/roles.addUserToRole', { method:'POST', body: JSON.stringify({ roleId, username }) });

// after - treat already-in-role as idempotent success
try {
  await fetch('/api/v1/roles.addUserToRole', { method:'POST', body: JSON.stringify({ roleId, username }) });
} catch (e) {
  if (e.error === 'error-user-already-in-role') return; // already desired state
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await fetch('/api/v1/roles.addUserToRole', { method:'POST', body: JSON.stringify({ roleId, username }) });
} catch (e) {
  if (e.error === 'error-user-already-in-role') return; // idempotent success
  throw e;
}

Prevention

When it happens

Trigger: POST /api/v1/roles.addUserToRole with a user+role (+optional roomId scope) combination that the user is already a member of; re-running an idempotent assignment.

Common situations: UI 'add to role' button clicked twice; automation re-applies role assignments; migration script re-granting existing roles.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/21a6aacc1620292d. Report an issue: GitHub.