calcom/cal.diy · error · UnauthorizedException
ApiAuthStrategy - third-party token - No user or team owner
Error message
ApiAuthStrategy - third-party token - No user or team owner associated with the token.
What it means
Thrown by validateThirdPartyAccessToken at the end of resolution when `user` is still null. Either the decoded token had a userId that didn't match a User, or it had a teamId whose owner lookup also failed, or it had neither claim. The token decoded successfully but resolved to no principal.
Source
Thrown at apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts:350
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
- Re-issue the third-party token after confirming the embedded userId (or teamId + its owner) still exists.
- Inspect the decoded token payload to confirm it contains a valid userId or teamId claim.
- Switch to platform OAuth access tokens for first-class support and clearer errors.
Defensive patterns
Strategy: validation
Validate before calling
const decoded = decodeThirdPartyToken(token);
if (!decoded?.userId && !decoded?.teamId) {
throw new Error('Third-party token has neither userId nor teamId; re-issue it');
}
if (decoded.userId) {
const u = await db.user.findUnique({ where: { id: decoded.userId } });
if (!u) throw new Error('Third-party token userId does not match an existing user');
} Type guard
function isThirdPartyTokenPayload(p: unknown): p is { userId?: number; teamId?: number } {
if (typeof p !== 'object' || p === null) return false;
const o = p as any;
return typeof o.userId === 'number' || typeof o.teamId === 'number';
} Prevention
- Issue third-party tokens with a verified userId rather than relying on team owner lookup.
- Re-issue tokens whenever the embedded user/team is restructured or deleted.
- Prefer first-class platform OAuth access tokens where possible.
When it happens
Trigger: A third-party token whose userId refers to a deleted user AND (no teamId, or the teamId owner also fails). Also when the decoded token carries neither userId nor teamId.
Common situations: User deleted from the org; tampered token; integration built against a different deployment's user/team ids.
Related errors
- ApiAuthStrategy - access token - User associated with the ac
- ApiAuthStrategy - next auth - User associated with the authe
- ApiAuthStrategy - third-party token - No owner found for the
- NextAuthStrategy - User associated with the authentication t
- ApiAuthStrategy - access token - Invalid Access Token.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/fa3acc929a3e40ea.
Report an issue: GitHub.