{"record":{"id":"fa3acc929a3e40ea","repo":"calcom/cal.diy","slug":"apiauthstrategy-third-party-token-no-user-or-t","errorCode":null,"errorMessage":"ApiAuthStrategy - third-party token - No user or team owner associated with the token.","messagePattern":"ApiAuthStrategy - third-party token - No user or team owner associated with the token\\.","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts","lineNumber":350,"sourceCode":"    if (decodedToken.userId) {\n      user = await this.userRepository.findByIdWithProfile(decodedToken.userId);\n      if (user) {\n        organizationId = this.usersService.getUserMainOrgId(user) as number;\n      }\n    } else if (decodedToken.teamId) {\n      const teamOwner = await this.userRepository.findOwnerByTeamIdWithProfile(decodedToken.teamId);\n      if (!teamOwner) {\n        throw new UnauthorizedException(\n          \"ApiAuthStrategy - third-party token - No owner found for the associated team.\"\n        );\n      }\n      user = teamOwner;\n      organizationId =\n        teamOwner.profiles?.find((p) => p.organizationId === decodedToken.teamId)?.organizationId ?? null;\n    }\n\n    if (!user) {\n      throw new UnauthorizedException(\n        \"ApiAuthStrategy - third-party token - No user or team owner associated with the token.\"\n      );\n    }\n\n    request.organizationId = organizationId;\n    return { success: true, data: user };\n  }\n}\n","sourceCodeStart":332,"sourceCodeEnd":359,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts#L332-L359","documentation":"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.","triggerScenarios":"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.","commonSituations":"User deleted from the org; tampered token; integration built against a different deployment's user/team ids.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const decoded = decodeThirdPartyToken(token);\nif (!decoded?.userId && !decoded?.teamId) {\n  throw new Error('Third-party token has neither userId nor teamId; re-issue it');\n}\nif (decoded.userId) {\n  const u = await db.user.findUnique({ where: { id: decoded.userId } });\n  if (!u) throw new Error('Third-party token userId does not match an existing user');\n}","typeGuard":"function isThirdPartyTokenPayload(p: unknown): p is { userId?: number; teamId?: number } {\n  if (typeof p !== 'object' || p === null) return false;\n  const o = p as any;\n  return typeof o.userId === 'number' || typeof o.teamId === 'number';\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["auth","third-party-token","user","platform"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}