calcom/cal.diy · warning · BadRequestException
{error_description}
Error message
{error_description} What it means
Thrown by ConferencingController.save when the OAuth provider redirected back with an `error` query param, indicating the user denied consent or the provider encountered an error during authorization. The thrown message is the raw error_description query value from the provider (e.g. 'The user denied access' from Zoom). Returns HTTP 400. Note: the outer try/catch in save swallows this and redirects to onErrorReturnTo, so end users see a redirect rather than the 400.
Source
Thrown at apps/api/v2/src/modules/conferencing/controllers/conferencing.controller.ts:154
description: "Conferencing application type",
enum: [ZOOM, OFFICE_365_VIDEO],
required: true,
})
async save(
@Query("state") state: string,
@Param("app") app: string,
@Query("code") code: string,
@Query("error") error: string | undefined,
@Query("error_description") error_description: string | undefined
): Promise<{ url: string }> {
if (!state) {
throw new BadRequestException("Missing `state` query param");
}
const decodedCallbackState: OAuthCallbackState = JSON.parse(state);
try {
if (error) {
throw new BadRequestException(error_description);
}
if (decodedCallbackState.teamId && decodedCallbackState.orgId) {
const apiUrl = this.config.get("api.url");
const url = `${apiUrl}/organizations/${decodedCallbackState.orgId}/teams/${decodedCallbackState.teamId}/conferencing/${app}/oauth/callback`;
const params: Record<string, string | undefined> = { state, code, error, error_description };
const headers = {
Authorization: `Bearer ${decodedCallbackState.accessToken}`,
};
try {
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 };
}
}View on GitHub (pinned to 176037d0af)
Solutions
- Handle the redirect gracefully in the UI — the user lands on onErrorReturnTo with no credential; show a 'connection cancelled' message.
- If the error is access_denied, prompt the user to retry and approve consent.
- For redirect_uri mismatches, fix the URI in the provider app console to exactly match what Cal.com sends.
- For Microsoft admin-consent errors, have a global admin pre-approve the app or grant admin consent.
Example fix
// before: client ignores error_description
if (response.status === 400) { alert('Connection failed'); }
// after
if (response.status === 400) {
const reason = response.data?.message || 'connection_failed';
showToast(reason.includes('denied') ? 'You cancelled Google Meet connection.' : `Provider error: ${reason}`);
} Defensive patterns
Strategy: fallback
Validate before calling
// Pre-flight: validate the OAuth request is well-formed before redirecting.
function buildAuthorizeUrl(app: string, scopes: string[], redirectUri: string, state: string): string {
if (!state) throw new Error('Cannot start OAuth flow without a valid session state.');
return `${authorizeBaseUrl}?response_type=code&client_id=${clientId}&scope=${scopes.join(' ')}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}`;
} Type guard
function isProviderErrorQuery(q: URLSearchParams): q is URLSearchParams {
return q.has('error');
} Try / catch
// The controller already swallows this and redirects to onErrorReturnTo.
// On the client, detect the redirect-without-credential outcome:
if (!connectedApps.includes(app)) {
showToast('Connection was cancelled or failed. Please try again.');
} Prevention
- Make onErrorReturnTo a route that shows a clear 'connection failed/cancelled' UI.
- For access_denied specifically, show a retry CTA; for other errors, log the provider reason.
- Verify the OAuth client is out of development mode and the user is allowlisted.
- Confirm admin consent is granted for Microsoft enterprise apps.
When it happens
Trigger: User clicked 'Cancel'/'Deny' on the Zoom or Microsoft consent screen; the provider rate-limited the authorization; redirect_uri mismatch caused the provider to return an error redirect; app not approved in a locked-down Workspace.
Common situations: User cancelled consent; Zoom app is still in 'development' mode and the user is not on the allowlist; Microsoft admin consent required but not granted; clock skew or expired authorize request.
Related errors
- Missing `state` query param
- Invalid conferencing app, available apps are:
- Invalid conferencing app. Available apps: GOOGLE_MEET.
- ${responseBody.error}
- Invalid Access token.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/5b41f81bad3ec00f.
Report an issue: GitHub.