calcom/cal.diy · error · UnauthorizedException
ApiAuthStrategy - third-party token - No owner found for the
Error message
ApiAuthStrategy - third-party token - No owner found for the associated team.
What it means
Thrown by validateThirdPartyAccessToken when a third-party (decoded) access token carries a teamId but findOwnerByTeamIdWithProfile(teamId) returns no owner. Cal.com resolves a team-scoped third-party token to the team's owner user; if the team has no owner membership, auth cannot proceed.
Source
Thrown at apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts:340
request: ApiAuthGuardRequest
): Promise<{ success: true; data: UserWithProfile } | { success: false }> {
const decodedToken = this.tokensService.getDecodedThirdPartyAccessToken(token);
if (!decodedToken) {
return { success: false };
}
let user: UserWithProfile | null = null;
let organizationId: number | null = null;
if (decodedToken.userId) {
user = await this.userRepository.findByIdWithProfile(decodedToken.userId);
if (user) {
organizationId = this.usersService.getUserMainOrgId(user) as number;
}
} else if (decodedToken.teamId) {
const teamOwner = await this.userRepository.findOwnerByTeamIdWithProfile(decodedToken.teamId);
if (!teamOwner) {
throw new UnauthorizedException(
"ApiAuthStrategy - third-party token - No owner found for the associated team."
);
}
user = teamOwner;
organizationId =
teamOwner.profiles?.find((p) => p.organizationId === decodedToken.teamId)?.organizationId ?? null;
}
if (!user) {
throw new UnauthorizedException(
"ApiAuthStrategy - third-party token - No user or team owner associated with the token."
);
}
request.organizationId = organizationId;
return { success: true, data: user };
}
}View on GitHub (pinned to 176037d0af)
Solutions
- Ensure the team referenced by the token has at least one user with the OWNER role membership.
- If the team no longer exists, mint a new third-party token for an existing team or for a specific userId instead.
- Prefer userId-scoped third-party tokens over teamId-scoped ones to avoid the owner-resolution step.
Defensive patterns
Strategy: validation
Validate before calling
const team = await db.team.findUnique({ where: { id: teamId }, include: { members: { where: { role: 'OWNER' } } } });
if (!team || team.members.length === 0) {
throw new Error(`Team ${teamId} has no owner; cannot resolve third-party token`);
} Prevention
- Keep at least one OWNER membership on every team that issues third-party tokens.
- Prefer userId-scoped third-party tokens to avoid owner resolution.
- On team deletion, invalidate tokens carrying its teamId.
When it happens
Trigger: An integration submits a third-party token whose teamId references a team with no OWNER-role membership (team deleted, ownership transferred and dangling, or a fabricated token with an arbitrary teamId).
Common situations: Team was deleted leaving an orphan teamId in the token; an org-restructuring removed the last owner; tokens issued before ownership was assigned.
Related errors
- ApiAuthStrategy - third-party token - No user or team owner
- RolesGuard - user with id=${user.id} does not have the minim
- RolesGuard - User is not a member of the team with id=${team
- RolesGuard - User is not part of the organization with id=${
- RolesGuard - User is not part of the team with id=${teamId}
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/8aff6aede9b8bbd9.
Report an issue: GitHub.