{"record":{"id":"464e5772eadfa2b7","repo":"calcom/cal.diy","slug":"invalid-conferencing-app-available-apps-are","errorCode":null,"errorMessage":"Invalid conferencing app, available apps are: ","messagePattern":"Invalid conferencing app, available apps are: ","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"apps/api/v2/src/modules/conferencing/services/conferencing.service.ts","lineNumber":78,"sourceCode":"  ) {\n    const userId = await this.tokensRepository.getAccessTokenOwnerId(decodedCallbackState.accessToken);\n    if (!userId) {\n      throw new UnauthorizedException(\"Invalid Access token.\");\n    }\n    switch (app) {\n      case ZOOM:\n        return await this.zoomVideoService.connectZoomApp(decodedCallbackState, code, userId, teamId);\n\n      case OFFICE_365_VIDEO:\n        return await this.office365VideoService.connectOffice365App(\n          decodedCallbackState,\n          code,\n          userId,\n          teamId\n        );\n\n      default:\n        throw new BadRequestException(\n          \"Invalid conferencing app, available apps are: \",\n          [ZOOM, OFFICE_365_VIDEO].join(\", \")\n        );\n    }\n  }\n\n  async getUserDefaultConferencingApp(userId: number) {\n    const user = await this.usersRepository.findById(userId);\n    return userMetadata.parse(user?.metadata)?.defaultConferencingApp;\n  }\n\n  async checkAppIsValidAndConnected(user: UserWithProfile, appSlug: string) {\n    if (!CONFERENCING_APPS.includes(appSlug)) {\n      throw new BadRequestException(\"Invalid app, available apps are: \", CONFERENCING_APPS.join(\", \"));\n    }\n    const credentials = await getUsersCredentialsIncludeServiceAccountKey(user);\n\n    const foundApp = getApps(credentials, true).filter((app) => app.slug === appSlug)[0];","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/conferencing/services/conferencing.service.ts#L60-L96","documentation":"Thrown by ConferencingService.connectOauthApps when the app slug in the OAuth callback is neither ZOOM nor OFFICE_365_VIDEO — the two apps that support OAuth connect via this path. Returns HTTP 400. Note the message string ends with a trailing space and the actual app list is passed as the second BadRequestException argument (NestJS error cause), so a bare toString() of the error will show only the leading text.","triggerScenarios":"OAuth callback invoked with app=google_meet (Google Meet does not use OAuth connect — see error 92), app=cal_video (built-in, no connect), or any unknown slug. Also if the app param is URL-decoded incorrectly.","commonSituations":"Provider app console redirect URI uses a different slug than Cal.com expects; client reused a callback URL for a different app; slug casing mismatch (Zoom vs zoom).","solutions":["Match the slug exactly to the constants ZOOM or OFFICE_365_VIDEO from @calcom/platform-constants.","Confirm the redirect URI template in Zoom/Microsoft uses the same lowercase slug Cal.com uses.","For Google Meet, use POST /v2/conferencing/google_meet/connect, not the OAuth callback.","When surfacing the error, read the second argument (error cause) to get the full valid-app list."],"exampleFix":"// before: client hardcodes slug\nwindow.location = `/v2/conferencing/Zoom/oauth/callback?...`;\n\n// after\nimport { ZOOM } from '@calcom/platform-constants';\nwindow.location = `/v2/conferencing/${ZOOM}/oauth/callback?...`;","handlingStrategy":"type-guard","validationCode":"import { ZOOM, OFFICE_365_VIDEO } from '@calcom/platform-constants';\nconst OAUTH_CONNECT_APPS = [ZOOM, OFFICE_365_VIDEO];\n\nfunction validateOauthConnectApp(app: string): void {\n  if (!OAUTH_CONNECT_APPS.includes(app)) {\n    throw new Error(`${app} is not an OAuth-connectable app. Valid: ${OAUTH_CONNECT_APPS.join(', ')}`);\n  }\n}\n\nvalidateOauthConnectApp(req.params.app);","typeGuard":"import { ZOOM, OFFICE_365_VIDEO } from '@calcom/platform-constants';\nconst OAUTH_CONNECT_APPS = [ZOOM, OFFICE_365_VIDEO] as const;\ntype OauthConnectApp = typeof OAUTH_CONNECT_APPS[number];\n\nfunction isOauthConnectApp(app: string): app is OauthConnectApp {\n  return (OAUTH_CONNECT_APPS as readonly string[]).includes(app);\n}","tryCatchPattern":"try {\n  await conferencingService.connectOauthApps(app, code, state);\n} catch (e) {\n  if (e instanceof BadRequestException && /Invalid conferencing app/.test(e.message)) {\n    return res.status(400).json({ code: 'invalid_app', supported: [ZOOM, OFFICE_365_VIDEO] });\n  }\n  throw e;\n}","preventionTips":["Source app slugs from the platform-constants package, not from literals.","Verify the redirect URI template in the provider console uses the same slug casing.","Read the error-cause argument (the second BadRequestException arg) to surface the full valid list."],"tags":["conferencing","zoom","office365","validation","nestjs","oauth"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}