calcom/cal.diy · error · BadRequestException
OAuth client with ID '${clientId}' not found
Error message
OAuth client with ID '${clientId}' not found What it means
Thrown by the POST /authorize OAuth flow endpoint after NextAuthGuard has authenticated the session. The handler calls oauthClientRepository.getOAuthClient(clientId) using the :clientId route param; a null/undefined return triggers BadRequestException (HTTP 400). Note the status is 400 BadRequest, not 404 NotFound, even though the semantic is 'client does not exist' — this is intentional so callers cannot distinguish 'missing client' from 'bad client id'.
Source
Thrown at apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts:60
constructor(
private readonly oauthClientRepository: OAuthClientRepository,
private readonly tokensRepository: TokensRepository,
private readonly oAuthFlowService: OAuthFlowService
) {}
@Post("/authorize")
@HttpCode(HttpStatus.OK)
@UseGuards(NextAuthGuard)
@DocsExcludeEndpoint()
async authorize(
@Param("clientId") clientId: string,
@Body() body: OAuthAuthorizeInput,
@GetUser("id") userId: number,
@Response() res: ExpressResponse
): Promise<void> {
const oauthClient = await this.oauthClientRepository.getOAuthClient(clientId);
if (!oauthClient) {
throw new BadRequestException(`OAuth client with ID '${clientId}' not found`);
}
if (!isOriginAllowed(body.redirectUri, oauthClient.redirectUris)) {
throw new BadRequestException("Invalid 'redirect_uri' value.");
}
const alreadyAuthorized = await this.tokensRepository.getAuthorizationTokenByClientUserIds(
clientId,
userId
);
if (alreadyAuthorized) {
throw new BadRequestException(
`User with id=${userId} has already authorized client with id=${clientId}.`
);
}
const { id } = await this.tokensRepository.createAuthorizationToken(clientId, userId);View on GitHub (pinned to 176037d0af)
Solutions
- Verify the clientId in the request path matches an id returned by GET /v2/oauth-clients for the same environment.
- Check the OAuth client still exists and is enabled in the org's client list before showing the authorize screen.
- If integrating, store clientIds in environment-scoped config rather than hardcoding.
- Treat HTTP 400 on /authorize as 'invalid client id' and re-fetch the client list in the UI.
Example fix
// before
const res = await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body: JSON.stringify({ redirectUri }) });
// after — verify the client exists in this environment first
const clients = await (await fetch('/v2/oauth-clients')).json();
if (!clients.data.some((c) => c.clientId === clientId)) {
throw new Error('OAuth client not available in this environment');
}
const res = await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body: JSON.stringify({ redirectUri }) }); Defensive patterns
Strategy: validation
Validate before calling
// Before calling /authorize, confirm the client exists in this environment
async function assertClientExists(clientId: string, headers: HeadersInit) {
const res = await fetch('/v2/oauth-clients', { headers });
const { data } = await res.json();
if (!data.some((c) => c.clientId === clientId)) {
throw new Error(`OAuth client ${clientId} not available in this environment`);
}
} Type guard
function isOAuthClientRef(value: unknown): value is { clientId: string } {
return typeof value === 'object' && value !== null && typeof (value as any).clientId === 'string';
} Try / catch
try {
await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body, headers });
} catch (e) {
if (e instanceof BadRequestException && /not found/.test(e.message)) {
// refresh client list and prompt user to re-select
}
throw e;
} Prevention
- Store OAuth client ids in environment-scoped config, not hardcoded.
- Re-fetch the client list when authorize returns 400.
- Strip trailing whitespace from copied client ids.
When it happens
Trigger: POST /v2/oauth-clients/:clientId/authorize where :clientId is a non-existent, deleted, or wrong-environment client id (e.g. staging id sent to production). The authenticated NextAuth user is valid; only the clientId lookup fails.
Common situations: Copy/paste of a clientId that has a typo or trailing space; using a clientId from a different environment; the OAuth client was deleted by an admin between when the frontend fetched the list and when the user clicked authorize; integration tests that hardcode ids against a freshly-reset database.
Related errors
- ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNe
- teamId is required for team events, please provide a valid t
- username is required for non-team events, please provide a v
- Invalid conferencing app, available apps are:
- CustomThrottlerGuard - Too many requests. Please try again l
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/8b88854787af1fbe.
Report an issue: GitHub.