RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-roleId
error-invalid-roleId
Error message
error-invalid-roleId
What it means
Thrown by GET /api/v1/roles.getUsersInRole when Roles.findOneById(role) returns null. Critically this lookup is by _id only — unlike roles.delete, which accepts id OR name. Passing a human-readable role name like 'admin' or 'moderator' here will always fail with error-invalid-roleId even though the role exists.
Solutions
- Resolve the name to an _id first: GET /api/v1/roles.list returns every role with both fields
- Use the 17-char role _id in the role param
- If the id came from config, refresh it against roles.list after workspace rebuilds
Example fix
// before
await sdk.get('roles.getUsersInRole', { role: 'admin' }); // name → error
// after
const { roles } = await sdk.get('roles.list');
const target = roles.find((r) => r.name === 'admin');
if (!target) throw new Error('unknown role name');
await sdk.get('roles.getUsersInRole', { role: target._id }); Defensive patterns
Strategy: validation
Validate before calling
// resolve to the role's _id before querying users
const { roles } = await sdk.get('roles.list');
const target = roles.find((r) => r._id === roleParam || r.name === roleParam);
if (!target) throw new Error(`unknown role: ${roleParam}`);
await sdk.get('roles.getUsersInRole', { role: target._id }); Type guard
const isRoleId = (v: unknown): v is string =>
typeof v === 'string' && /^[A-Za-z0-9]{17}$/.test(v); Try / catch
catch 'error-invalid-roleId', re-resolve the param against roles.list (the role may have been renamed/deleted), and retry once with the fresh _id.
Prevention
- Remember this endpoint matches _id only, unlike roles.delete which also takes names
- Cache the roles.list mapping and refresh it after workspace changes
- Never hardcode role ids across environments — resolve by name at runtime
When it happens
Trigger: GET /api/v1/roles.getUsersInRole?role=admin (a name, not an id); a deleted custom role's id still referenced by cached scripts; a typo'd or truncated role _id.
Common situations: Code written against roles.delete (name-friendly) reused for getUsersInRole; role ids hardcoded per environment and stale after a re-install; names and ids conflated in configuration files.
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/307d2f3962f1a4d4.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/roles.ts:200
username: 1,
emails: 1,
avatarETag: 1,
createdAt: 1,
_updatedAt: 1,
};
if (!role) {
throw new Meteor.Error('error-param-not-provided', 'Query param "role" is required');
}
if (roomId && !(await hasPermissionAsync(this.user, 'view-other-user-channels'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
const options = { projection: { _id: 1 } };
const roleData = await Roles.findOneById<Pick<IRole, '_id'>>(role, options);
if (!roleData) {
throw new Meteor.Error('error-invalid-roleId');
}
const { cursor, totalCount } = await getUsersInRolePaginated(roleData._id, roomId, {
limit: count,
sort: { username: 1 },
skip: offset,
projection,
});
const [users, total] = await Promise.all([cursor.toArray(), totalCount]);
return API.v1.success({ users, total });
},
)
.post(
'roles.delete',
{
authRequired: true,View on GitHub (pinned to b2c16d5842)