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

  1. Handle the redirect gracefully in the UI — the user lands on onErrorReturnTo with no credential; show a 'connection cancelled' message.
  2. If the error is access_denied, prompt the user to retry and approve consent.
  3. For redirect_uri mismatches, fix the URI in the provider app console to exactly match what Cal.com sends.
  4. 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

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


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/5b41f81bad3ec00f. Report an issue: GitHub.