{"record":{"id":"8b88854787af1fbe","repo":"calcom/cal.diy","slug":"oauth-client-with-id-clientid-not-found","errorCode":null,"errorMessage":"OAuth client with ID '${clientId}' not found","messagePattern":"OAuth client with ID '(.+?)' not found","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts","lineNumber":60,"sourceCode":"  constructor(\n    private readonly oauthClientRepository: OAuthClientRepository,\n    private readonly tokensRepository: TokensRepository,\n    private readonly oAuthFlowService: OAuthFlowService\n  ) {}\n\n  @Post(\"/authorize\")\n  @HttpCode(HttpStatus.OK)\n  @UseGuards(NextAuthGuard)\n  @DocsExcludeEndpoint()\n  async authorize(\n    @Param(\"clientId\") clientId: string,\n    @Body() body: OAuthAuthorizeInput,\n    @GetUser(\"id\") userId: number,\n    @Response() res: ExpressResponse\n  ): Promise<void> {\n    const oauthClient = await this.oauthClientRepository.getOAuthClient(clientId);\n    if (!oauthClient) {\n      throw new BadRequestException(`OAuth client with ID '${clientId}' not found`);\n    }\n\n    if (!isOriginAllowed(body.redirectUri, oauthClient.redirectUris)) {\n      throw new BadRequestException(\"Invalid 'redirect_uri' value.\");\n    }\n\n    const alreadyAuthorized = await this.tokensRepository.getAuthorizationTokenByClientUserIds(\n      clientId,\n      userId\n    );\n\n    if (alreadyAuthorized) {\n      throw new BadRequestException(\n        `User with id=${userId} has already authorized client with id=${clientId}.`\n      );\n    }\n\n    const { id } = await this.tokensRepository.createAuthorizationToken(clientId, userId);","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/oauth-clients/controllers/oauth-flow/oauth-flow.controller.ts#L42-L78","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body: JSON.stringify({ redirectUri }) });\n\n// after — verify the client exists in this environment first\nconst clients = await (await fetch('/v2/oauth-clients')).json();\nif (!clients.data.some((c) => c.clientId === clientId)) {\n  throw new Error('OAuth client not available in this environment');\n}\nconst res = await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body: JSON.stringify({ redirectUri }) });","handlingStrategy":"validation","validationCode":"// Before calling /authorize, confirm the client exists in this environment\nasync function assertClientExists(clientId: string, headers: HeadersInit) {\n  const res = await fetch('/v2/oauth-clients', { headers });\n  const { data } = await res.json();\n  if (!data.some((c) => c.clientId === clientId)) {\n    throw new Error(`OAuth client ${clientId} not available in this environment`);\n  }\n}","typeGuard":"function isOAuthClientRef(value: unknown): value is { clientId: string } {\n  return typeof value === 'object' && value !== null && typeof (value as any).clientId === 'string';\n}","tryCatchPattern":"try {\n  await fetch(`/v2/oauth-clients/${clientId}/authorize`, { method: 'POST', body, headers });\n} catch (e) {\n  if (e instanceof BadRequestException && /not found/.test(e.message)) {\n    // refresh client list and prompt user to re-select\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["oauth","nestjs","api-v2","validation","client-id"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}