calcom/cal.diy · error · OAuth2HttpException
${reason}
Error message
${reason} What it means
Thrown by OAuth2ErrorService.handleAuthorizeError when the underlying error is an ErrorWithCode whose data.reason is one of the NON_REDIRECTABLE_REASONS (client_not_found, client_not_approved, client_rejected, redirect_uri_mismatch). Instead of redirecting the user back to the redirect_uri, the server responds directly with an OAuth2HttpException carrying error=err.message and error_description=reason. This is the OAuth2 /authorize endpoint's 'fatal client misconfiguration' path where redirecting would be unsafe or impossible.
Source
Thrown at apps/api/v2/src/modules/auth/oauth2/services/oauth2-error.service.ts:26
"client_not_found",
"client_not_approved",
"client_rejected",
"redirect_uri_mismatch",
]);
@Injectable()
export class OAuth2ErrorService {
private readonly logger = new Logger("OAuth2ErrorService");
constructor(private readonly oAuthService: OAuthService) {}
handleAuthorizeError(err: unknown, redirectUri: string, state?: string): never {
if (err instanceof ErrorWithCode) {
const reason = err.data?.["reason"] as string | undefined;
if (reason && NON_REDIRECTABLE_REASONS.has(reason)) {
const statusCode = getHttpStatusCode(err);
throw new OAuth2HttpException(
{
error: err.message,
error_description: reason,
},
statusCode
);
}
}
const errorRedirectUrl = this.oAuthService.buildErrorRedirectUrl(redirectUri, err, state);
throw new OAuth2RedirectException(errorRedirectUrl);
}
handleTokenError(err: unknown): never {
if (err instanceof ErrorWithCode) {
const statusCode = getHttpStatusCode(err);
const reason = err.data?.["reason"] as string | undefined;
throw new OAuth2HttpException(View on GitHub (pinned to 176037d0af)
Solutions
- Verify the clientId in the authorize URL matches the value shown at https://app.cal.com/settings/platform for the target environment.
- Confirm the client is in APPROVED state in the platform OAuth settings; re-request approval if it is PENDING or REJECTED.
- Make the redirect_uri query parameter exactly match one of the URIs registered under the client's 'Redirect uris' list (scheme, host, port, and path must all match).
- Inspect the error_description field in the response; it will be the literal reason (client_not_found / client_not_approved / client_rejected / redirect_uri_mismatch) telling you which condition failed.
Example fix
// before
const url = `https://api.cal.com/v2/oauth/${clientId}/authorize?redirect_uri=http://localhost:3000&state=xyz`;
// after — redirect_uri must match a registered URI exactly
const url = `https://api.cal.com/v2/oauth/${clientId}/authorize?redirect_uri=http://localhost:3000/auth/callback&state=xyz`; Defensive patterns
Strategy: validation
Validate before calling
// Before redirecting to /authorize, validate the client config you control
const CLIENT_ID = process.env.CAL_OAUTH_CLIENT_ID!;
const REDIRECT_URI = process.env.CAL_REDIRECT_URI!; // must be registered exactly
const APPROVED_STATES = new Set(['APPROVED']);
async function preflightAuthorize() {
const client = await fetch(`/v2/oauth-clients/${CLIENT_ID}`, {
headers: { Authorization: `Bearer ${process.env.CAL_ADMIN_API_KEY}` }
}).then(r => r.ok ? r.json() : null);
if (!client) throw new Error('client_not_found: check CAL_OAUTH_CLIENT_ID');
if (!APPROVED_STATES.has(client.approvalStatus)) throw new Error(`client_not_approved: status=${client.approvalStatus}`);
if (!client.redirectUris.includes(REDIRECT_URI)) throw new Error(`redirect_uri_mismatch: register ${REDIRECT_URI}`);
} Type guard
import { ErrorWithCode } from '@calcom/platform-libraries/errors';
const NON_REDIRECTABLE_REASONS = new Set(['client_not_found','client_not_approved','client_rejected','redirect_uri_mismatch']);
function isNonRedirectableAuthorizeError(err: unknown): err is ErrorWithCode {
return err instanceof ErrorWithCode
&& NON_REDIRECTABLE_REASONS.has((err as any).data?.['reason']);
} Try / catch
try {
await driveAuthorizeFlow();
} catch (err) {
if (isNonRedirectableAuthorizeError(err)) {
// surfaced as HTTP error, not redirect — show config guidance to the integrator
renderClientConfigError((err as any).data.reason);
} else {
// redirect-based error — handle in the redirect_uri callback
}
} Prevention
- Register every redirect_uri variant (http/https, trailing slash) you will use before going live.
- Keep client id/secret in environment variables keyed per environment to avoid staging/production mix-ups.
- Add a preflight check that the client is APPROVED before directing users to /authorize.
When it happens
Trigger: A GET to the /v2/oauth/{clientId}/authorize endpoint where: (a) clientId does not match any OAuth client row (client_not_found); (b) the client exists but its approval state is not approved (client_not_approved / client_rejected); (c) the redirect_uri query param does not match any registered redirect URI for the client (redirect_uri_mismatch). The error surfaces as a JSON HTTP error response rather than a 302 redirect.
Common situations: Developer copy-pastes the wrong client id from settings; client is still in PENDING approval after creation; redirect_uri in the request uses http://localhost:3000 while the registered URI is http://localhost:3000/callback (trailing path or scheme mismatch); staging client id used against production, or vice versa.
Related errors
- ${reason ?? err.message}
- CustomThrottlerGuard - Invalid API Key
- ApiKeysService - This endpoint can only be accessed using an
- ApiKeysService - No API key provided
- ApiKeysService - provided api key is not valid.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/47a556a702ac794b.
Report an issue: GitHub.