calcom/cal.diy · error · BadRequestException
Invalid Access token.
Error message
Invalid Access token.
What it means
Thrown by StripeController.save on the user-level fallback path. The controller resolves the owner of the access token embedded in state via tokensRepository.getAccessTokenOwnerId; if no token owner is found, it throws 400 BadRequest 'Invalid Access token.' Note this is a BadRequest in the controller, distinct from the UnauthorizedException with the same message thrown later inside StripeService.saveStripeAccount.
Source
Thrown at apps/api/v2/src/modules/stripe/controllers/stripe.controller.ts:136
const fallbackUrl = decodedCallbackState.onErrorReturnTo || "";
return { url: fallbackUrl };
}
}
// user-level fallback
const userId = await this.tokensRepository.getAccessTokenOwnerId(decodedCallbackState.accessToken);
// user cancels flow
if (error === "access_denied") {
return { url: getOnErrorReturnToValueFromQueryState(state) };
}
if (error) {
throw new BadRequestException(stringify({ error, error_description }));
}
if (!userId) {
throw new BadRequestException("Invalid Access token.");
}
return await this.stripeService.saveStripeAccount(decodedCallbackState, code, userId);
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
}
return {
url: decodedCallbackState.onErrorReturnTo ?? "",
};
}
}
@Get("/check")
@UseGuards(ApiAuthGuard)
@HttpCode(HttpStatus.OK)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Check Stripe connection" })View on GitHub (pinned to 176037d0af)
Solutions
- Have the user re-authenticate to obtain a fresh access token before starting the Stripe connect flow.
- Restart the connect flow from /v2/stripe/redirect with a current Bearer token so state contains a valid accessToken.
- If persisting state across the redirect, ensure the token has a lifetime longer than the OAuth round-trip.
Defensive patterns
Strategy: validation
Validate before calling
async function ensureFreshToken(api) {
const me = await api.getMe().catch(() => null);
if (!me) {
// re-authenticate before starting the Stripe flow
redirectToLogin();
}
} Type guard
function hasUsableToken(token: string | null | undefined): token is string {
return typeof token === 'string' && token.length > 0 && !isExpired(token);
}
function isExpired(jwt: string) {
const payload = JSON.parse(atob(jwt.split('.')[1] ?? ''));
return Date.now() >= (payload.exp ?? 0) * 1000;
} Try / catch
try {
await api.completeStripeSave();
} catch (e) {
if (e.status === 400 && /Invalid Access token/.test(e.message)) {
await refreshToken();
await api.completeStripeSave(); // retry once with fresh token
} else throw e;
} Prevention
- Ensure the access token has a lifetime longer than the OAuth round-trip before starting the flow.
- Re-authenticate the user if their session is near expiry before launching Stripe connect.
- Do not reuse state JSON built with a stale token; regenerate state via /redirect.
When it happens
Trigger: The OAuth callback's state.accessToken is expired, revoked, belongs to a different environment, or was never issued, so getAccessTokenOwnerId returns null/falsy. teamId/orgId are absent so the user-level fallback is taken.
Common situations: The user's session expired during the Stripe OAuth round-trip. The access token was issued by a different API deployment. The token was manually crafted or corrupted in the state JSON. The token row was purged.
Related errors
- ApiAuthStrategy - access token - Invalid Access Token.
- PermissionsGuard - no oAuth client found for access token=${
- ApiAuthStrategy - access token - OAuth client not found give
- ApiAuthStrategy - access token - Invalid Access Token.. No o
- Invalid Access token.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/ef75dbd2176f5194.
Report an issue: GitHub.