calcom/cal.diy · error · ForbiddenException
RolesGuard - user with id=${user.id} does not have the minim
Error message
RolesGuard - user with id=${user.id} does not have the minimum required role=${allowedRole} within What it means
ForbiddenException from RolesGuard.throwForbiddenError — built dynamically to state that the authenticated user lacks the minimum required role within a specific organization and/or team. The message appends the org and/or team id depending on which scope was requested. Role checks use hasMinimumRole against ORG_ROLES / TEAM_ROLES hierarchies.
Source
Thrown at apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts:50
if (!canAccess) {
this.throwForbiddenError(user, orgId, teamId, allowedRole);
}
return true;
}
throwForbiddenError(user: ApiAuthGuardUser, orgId: string, teamId: string, allowedRole: string) {
let errorMessage = `RolesGuard - user with id=${user.id} does not have the minimum required role=${allowedRole} within`;
if (orgId) {
errorMessage += ` organization with id=${orgId}`;
}
if (teamId) {
errorMessage += ` team with id=${teamId}`;
}
errorMessage += `.`;
throw new ForbiddenException(errorMessage);
}
async checkUserRoleAccess(
user: ApiAuthGuardUser,
orgId: string,
teamId: string,
allowedRole: string
): Promise<{ canAccess: boolean }> {
const REDIS_CACHE_KEY = `apiv2:user:${user.id ?? "none"}:org:${orgId ?? "none"}:team:${
teamId ?? "none"
}:guard:roles:${allowedRole}`;
const cachedAccess = JSON.parse((await this.redisService.redis.get(REDIS_CACHE_KEY)) ?? "false");
if (cachedAccess) {
return { canAccess: cachedAccess };
}
let canAccess = false;View on GitHub (pinned to 176037d0af)
Solutions
- Have an org owner/admin promote the user to the required role in the Cal.com dashboard.
- Verify the request targets the correct orgId/teamId — the user may be sufficiently privileged in a different team.
- For SYSTEM_ADMIN_ROLE routes, confirm the user has the system admin flag in the database.
- Clear the Redis cache key apiv2:user:<id>:org:<orgId>:team:<teamId>:guard:roles:<role> if roles were just changed and the cached 'false' is stale.
Defensive patterns
Strategy: try-catch
Validate before calling
const allowed = ['ORG_ADMIN'];
const userRole = membership.role; // 'MEMBER' | 'ADMIN' | 'OWNER' | ...
const ok = hasMinimumRole({ checkRole: `ORG_${userRole}`, minimumRole: allowed[0], roles: ORG_ROLES });
if (!ok) { /* tell user they need elevation */ } Try / catch
try {
await client.get(`/v2/orgs/${orgId}/settings`);
} catch (e) {
if (e.status === 403 && /does not have the minimum required role/.test(e.message)) {
// prompt user to request role elevation from an owner
}
throw e;
} Prevention
- After elevating a user, clear the apiv2:user:…:guard:roles: Redis cache or wait it out.
- Verify you are operating on the right orgId/teamId.
- Seed Membership rows correctly in tests.
When it happens
Trigger: Calling a @Roles(...) / @MinimumRole(...) guarded endpoint where the user's membership role in the target org/team is below the allowed role — e.g. an ORG_MEMBER calling a route that requires ORG_ADMIN, or a TEAM_MEMBER calling a route requiring TEAM_OWNER.
Common situations: Promoting a user is pending/awaiting acceptance; user is a member of the parent org but not with a sufficient role; test fixtures assign the wrong role; team-only route called by an org-only admin where org-above-team escalation does not apply; SYSTEM_ADMIN_ROLE route hit by a non-system-admin.
Related errors
- RolesGuard - User is not part of the organization with id=${
- RolesGuard - User is not part of the team with id=${teamId}
- RolesGuard - User is not a member of the organization with i
- RolesGuard - User is not a member of the team with id=${team
- You are not authorized to book this event type. You must be
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/6d417090dd29763e.
Report an issue: GitHub.