calcom/cal.diy · error · UnauthorizedException
Invalid Access token.
Error message
Invalid Access token.
What it means
Thrown by StripeService.saveStripeAccount if the userId argument is falsy. This is a 401 Unauthorized defense-in-depth guard inside the service; the controller normally resolves userId from the access token and passes a real number, so reaching this throw means the service was called without a valid user.
Source
Thrown at apps/api/v2/src/modules/stripe/stripe.service.ts:102
async getStripeAppKeys() {
const app = await this.appsRepository.getAppBySlug("stripe");
const { client_id, client_secret } = stripeKeysResponseSchema.parse(app?.keys);
if (!client_id) {
throw new NotFoundException("Stripe app not found");
}
if (!client_secret) {
throw new NotFoundException("Stripe app not found");
}
return { client_id, client_secret };
}
async saveStripeAccount(state: OAuthCallbackState, code: string, userId: number): Promise<{ url: string }> {
if (!userId) {
throw new UnauthorizedException("Invalid Access token.");
}
const response = await stripeInstance.oauth.token({
grant_type: "authorization_code",
code: code?.toString(),
});
const data: StripeData = { ...response, default_currency: "" };
if (response["stripe_user_id"]) {
const account = await stripeInstance.accounts.retrieve(response["stripe_user_id"]);
data["default_currency"] = account.default_currency;
}
const existingCredentials = await this.credentialsRepository.findAllCredentialsByTypeAndUserId(
"stripe_payment",
userId
);
View on GitHub (pinned to 176037d0af)
Solutions
- Always resolve a non-zero userId (via getAccessTokenOwnerId) before calling saveStripeAccount.
- Route Stripe connect completion through the StripeController.save endpoint rather than calling the service directly.
- Type the service parameter as a branded/non-zero number so a falsy value is a compile error.
Example fix
// before
await stripeService.saveStripeAccount(state, code, maybeUserId);
// after
if (!userId) throw new UnauthorizedException('Sign in required');
await stripeService.saveStripeAccount(state, code, userId); Defensive patterns
Strategy: validation
Validate before calling
async function resolveUserForStripe(tokensRepository, accessToken) {
const userId = await tokensRepository.getAccessTokenOwnerId(accessToken);
if (!userId) throw new UnauthorizedException('Sign in required');
return userId;
} Type guard
function isResolvedUserId(userId: number | null | undefined): userId is number {
return typeof userId === 'number' && userId > 0;
} Try / catch
try {
await stripeService.saveStripeAccount(state, code, userId);
} catch (e) {
if (e.status === 401 && /Invalid Access token/.test(e.message)) {
// ensure caller resolves userId before invoking the service
} else throw e;
} Prevention
- Always resolve a non-zero userId in the controller before calling saveStripeAccount.
- Route Stripe completion through the controller, never call the service directly.
- Type the userId parameter so zero/undefined is a compile-time error.
When it happens
Trigger: saveStripeAccount(state, code, userId) is invoked with userId = 0, undefined, or null. In normal flow the controller resolves a userId first; this guard fires only when the service is called directly or the controller passed a falsy value through.
Common situations: A code path calls saveStripeAccount directly (bypassing the controller) without resolving a user. A refactor changed the controller to pass an optional userId that can be undefined. A test invokes the service without a user fixture.
Related errors
- Invalid Access token.
- ApiAuthStrategy - access token - Invalid Access Token.
- ApiAuthStrategy - access token - OAuth client not found give
- Missing `state` query param
- error=${error}&error_description=${error_description}
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/53317a91cda986f3.
Report an issue: GitHub.