calcom/cal.diy · error · ForbiddenException

RolesGuard - User is not a member of the organization with i

Error message

RolesGuard - User is not a member of the organization with id=${orgId}.

What it means

ForbiddenException from RolesGuard when checking role access for an org-scoped (orgId present, teamId absent) request and membershipRepository.findMembershipByOrgId(orgId, user.id) returns null — the user is not a member of that organization at all, so role comparison is skipped and a hard 403 is thrown.

Source

Thrown at apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts:93

    // System admin can access everything
    else if (user.isSystemAdmin) {
      this.logger.log(`User (${user.id}) is system admin, allowing access.`);
      canAccess = true;
    }

    // if the required role is SYSTEM_ADMIN_ROLE but user is not system admin, return false
    else if (allowedRole === SYSTEM_ADMIN_ROLE && !user.isSystemAdmin) {
      this.logger.log(`User (${user.id}) is not system admin, denying access.`);
      canAccess = false;
    }

    // Checking the role of the user within the organization
    else if (Boolean(orgId) && !Boolean(teamId)) {
      const membership = await this.membershipRepository.findMembershipByOrgId(Number(orgId), user.id);
      if (!membership) {
        this.logger.log(`User (${user.id}) is not a member of the organization (${orgId}), denying access.`);
        throw new ForbiddenException(
          `RolesGuard - User is not a member of the organization with id=${orgId}.`
        );
      }

      if (ORG_ROLES.includes(allowedRole as unknown as (typeof ORG_ROLES)[number])) {
        canAccess = hasMinimumRole({
          checkRole: `ORG_${membership.role}`,
          minimumRole: allowedRole,
          roles: ORG_ROLES,
        });
      }
    }

    // Checking the role of the user within the team
    else if (Boolean(teamId) && !Boolean(orgId)) {
      const membership = await this.membershipRepository.findMembershipByTeamId(Number(teamId), user.id);
      if (!membership) {
        this.logger.log(`User (${user.id}) is not a member of the team (${teamId}), denying access.`);

View on GitHub (pinned to 176037d0af)

Solutions

  1. Have an org admin invite the user and have the user accept the invitation.
  2. Confirm the orgId in the request matches an org the user actually belongs to.
  3. If the user was just added, the RolesGuard Redis cache (apiv2:user:…:guard:roles:…) may hold a stale 'false' — clear it.
  4. For tests, seed a Membership row for the user in that org.
Defensive patterns

Strategy: validation

Validate before calling

const membership = await membershipRepository.findMembershipByOrgId(Number(orgId), user.id);
if (!membership) {
  // prompt user to accept an org invitation before calling
}

Try / catch

try {
  await client.get(`/v2/orgs/${orgId}/teams`);
} catch (e) {
  if (e.status === 403 && /not a member of the organization/.test(e.message)) {
    // ask user to join the org
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling an org-scoped route (orgId set, no teamId) where the authenticated user has no Membership row linking them to that organization. The guard logs 'User (id) is not a member of the organization' before throwing.

Common situations: User was removed from the org; invitation never accepted; orgId typo/mismatch; cross-tenant access attempted; user belongs to a team inside a different org; test user not seeded into the org.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/966d72d82b25a605. Report an issue: GitHub.