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 /api/v1/roles.addUserToRole when hasRoleAsync(user._id, role._id, roomId) is already true — the target user already holds that role, globally or in the given room scope. The endpoint deliberately rejects double-assignment instead of being idempotent, so retries of a partially-succeeded flow or scripts that re-add existing roles hit this.
Solutions
- Make callers idempotent: catch error-user-already-in-role and treat it as success
- Pre-check with roles.getUsersInRole (needs access-permissions) or users.info before assigning
- Fix the upstream retry logic if the same assignment is being pushed repeatedly
Example fix
// before
await sdk.post('roles.addUserToRole', { roleId, username });
// after (idempotent assign)
try {
await sdk.post('roles.addUserToRole', { roleId, username });
} catch (e) {
if (e.error !== 'error-user-already-in-role') throw e;
// already assigned — nothing to do
} Defensive patterns
Strategy: try-catch
Validate before calling
// optional pre-check (global scope only — room-scoped grants are not in user.roles)
const { user } = await sdk.get('users.info', { username });
if (!roomId && user.roles?.includes(roleName)) return { alreadyAssigned: true }; Try / catch
wrap roles.addUserToRole in a catch that treats e.error === 'error-user-already-in-role' as success (idempotent assign); rethrow everything else.
Prevention
- Write provisioning scripts idempotently from the start
- Fix duplicate-trigger sources (double webhooks, retried syncs) rather than suppressing the error everywhere
- Remember room-scoped assignments are checked per roomId
When it happens
Trigger: POST roles.addUserToRole { username, roleId, roomId? } where the user already has the role: re-running an onboarding script; retrying after a network blip where the first attempt actually committed; adding a role in a room scope the user already holds there.
Common situations: Idempotency-unaware provisioning scripts (SCIM/LDAP sync) run twice; UI double-submit; migration re-applying role grants from a snapshot.
Related errors
- error-role-in-use
- error-user-not-in-role
- error-action-not-allowed
- error-action-not-allowed
- error-admin-required
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/21a6aacc1620292d.
Report an issue: GitHub.
Appendix: 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 b2c16d5842)