calcom/cal.diy · error · ForbiddenException

RolesGuard - User is not part of the team with id=${teamId}

Error message

RolesGuard - User is not part of the team with id=${teamId} and/or, is not an admin nor an owner of the organization with id=${orgId}.

What it means

ForbiddenException from RolesGuard in the combined org+team branch when: (a) the allowedRole is a TEAM role, (b) the user IS a member of the org but NOT as ORG_ADMIN/ORG_OWNER (so org-over-team escalation does not apply), and (c) the user has no teamMembership row. Because org admins/owners are auto-allowed for any team role, this only fires for non-admin org members who are also absent from the specific team.

Source

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

      const teamMembership = await this.membershipRepository.findMembershipByTeamId(Number(teamId), user.id);
      const orgMembership = await this.membershipRepository.findMembershipByOrgId(Number(orgId), user.id);

      if (!orgMembership) {
        this.logger.log(`User (${user.id}) is not part of the organization (${orgId}), denying access.`);
        throw new ForbiddenException(`RolesGuard - User is not part of the organization with id=${orgId}.`);
      }

      // if the role checked is a TEAM role
      if (TEAM_ROLES.includes(allowedRole as unknown as (typeof TEAM_ROLES)[number])) {
        // if the user is admin or owner of org, allow request because org > team
        if (`ORG_${orgMembership.role}` === "ORG_ADMIN" || `ORG_${orgMembership.role}` === "ORG_OWNER") {
          canAccess = true;
        } else {
          if (!teamMembership) {
            this.logger.log(
              `User (${user.id}) is not part of the team (${teamId}) and/or, is not an admin nor an owner of the organization (${orgId}).`
            );
            throw new ForbiddenException(
              `RolesGuard - User is not part of the team with id=${teamId} and/or, is not an admin nor an owner of the organization with id=${orgId}.`
            );
          }

          // if user is not admin nor an owner of org, and is part of the team, then check user team membership role
          canAccess = hasMinimumRole({
            checkRole: `TEAM_${teamMembership.role}`,
            minimumRole: allowedRole,
            roles: TEAM_ROLES,
          });
        }
      }

      // if allowed role is a ORG ROLE, check org membersip role
      else if (ORG_ROLES.includes(allowedRole as unknown as (typeof ORG_ROLES)[number])) {
        canAccess = hasMinimumRole({
          checkRole: `ORG_${orgMembership.role}`,
          minimumRole: allowedRole,

View on GitHub (pinned to 176037d0af)

Solutions

  1. Have a team admin/owner add the user to the team, OR have an org owner promote the user to ORG_ADMIN/ORG_OWNER (which grants cross-team access).
  2. Confirm the teamId matches a team inside the org the user belongs to.
  3. Clear the RolesGuard Redis cache if a membership/role was just updated.
  4. For tests, seed a team Membership row (or promote to ORG_ADMIN in the org Membership).
Defensive patterns

Strategy: validation

Validate before calling

const orgMembership = await membershipRepository.findMembershipByOrgId(Number(orgId), user.id);
const teamMembership = await membershipRepository.findMembershipByTeamId(Number(teamId), user.id);
const isOrgPrivileged = orgMembership && ['ADMIN','OWNER'].includes(orgMembership.role);
if (!isOrgPrivileged && !teamMembership) {
  // user must either be added to the team OR promoted to ORG_ADMIN/ORG_OWNER
}

Try / catch

try {
  await client.get(`/v2/orgs/${orgId}/teams/${teamId}/members`);
} catch (e) {
  if (e.status === 403 && /not part of the team.*not an admin nor an owner/.test(e.message)) {
    // request team membership OR org owner promotion
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling an org+team route requiring a TEAM_* role, where the user is e.g. an ORG_MEMBER of the org and has no Membership in the target team. The guard logs both the team-absence and the not-admin/owner context.

Common situations: Org member tries to manage a team they weren't added to; user was removed from the team but retained org membership; recent role change not yet reflected (cache); test seed has org Membership but not team Membership.

Related errors


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