calcom/cal.diy · error · BadRequestException
error=${error}&error_description=${error_description}
Error message
error=${error}&error_description=${error_description} What it means
Thrown by StripeController.save when the Stripe OAuth callback carries an `error` query parameter that is not the benign 'access_denied' (user-cancelled) value. The controller stringifies { error, error_description } and throws it as 400 BadRequest, so the response body contains the raw OAuth error details from Stripe.
Source
Thrown at apps/api/v2/src/modules/stripe/controllers/stripe.controller.ts:132
const response = await this.httpService.axiosRef.get(url, { params, headers });
const redirectUrl = response.data?.url || decodedCallbackState.onErrorReturnTo || "";
return { url: redirectUrl };
} catch (err) {
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")View on GitHub (pinned to 176037d0af)
Solutions
- Read error_description from the response body to get Stripe's explanation, then restart the flow from /v2/stripe/redirect.
- Verify the redirect URI registered in the Stripe dashboard exactly matches api.url + '/v2/stripe/save'.
- Confirm the Stripe app is active and the connecting account is in good standing in the Stripe dashboard.
Defensive patterns
Strategy: try-catch
Validate before calling
// The callback is server-side; clients handle the resulting redirect URL.
// Prevent by validating redirect URI + app config before starting the flow:
async function preflightStripe(api) {
const status = await api.getStripeStatus();
if (!status.appConfigured) throw new Error('Stripe app not configured');
} Try / catch
// Server-side controller already stringifies the OAuth error.
// On the client, inspect the returned url / error payload:
if (result.url && /error=/.test(result.url)) {
surfaceToUser('Stripe authorization failed; please reconnect.');
} Prevention
- Keep the redirect URI registered in Stripe identical to api.url + '/v2/stripe/save'.
- Ensure the authorization code is exchanged promptly (it expires) - avoid long pauses in the flow.
- Keep the Stripe app active and the connecting account in good standing.
When it happens
Trigger: Stripe redirects back to /v2/stripe/save?error=invalid_request&error_description=... (or invalid_grant, redirect_uri_mismatch, etc.) because something went wrong during authorization, and the value is not 'access_denied'.
Common situations: The authorization code was reused or expired. The redirect URI in the request does not match the one registered in the Stripe app. The Stripe app was deleted or restricted. The user's Stripe account lacks permission. A Stripe platform outage returned an error.
Related errors
- Missing `state` query param
- Invalid Access token.
- ApiAuthStrategy - access token - Invalid Access Token.
- ApiAuthStrategy - access token - OAuth client not found give
- Missing `state` query param
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/c9ccd457ecd07bac.
Report an issue: GitHub.