calcom/cal.diy · warning · BadRequestException

${appSlug} not connected.

Error message

${appSlug} not connected.

What it means

Thrown by ConferencingService.checkAppIsValidAndConnected when the slug is valid but no connected credential with an app location is found for the user. Returns HTTP 400. Used by disconnectConferencingApp (cannot disconnect what is not connected) and setDefaultConferencingApp (cannot default to a non-connected app, except cal_video which is global).

Source

Thrown at apps/api/v2/src/modules/conferencing/services/conferencing.service.ts:101

  }

  async getUserDefaultConferencingApp(userId: number) {
    const user = await this.usersRepository.findById(userId);
    return userMetadata.parse(user?.metadata)?.defaultConferencingApp;
  }

  async checkAppIsValidAndConnected(user: UserWithProfile, appSlug: string) {
    if (!CONFERENCING_APPS.includes(appSlug)) {
      throw new BadRequestException("Invalid app, available apps are: ", CONFERENCING_APPS.join(", "));
    }
    const credentials = await getUsersCredentialsIncludeServiceAccountKey(user);

    const foundApp = getApps(credentials, true).filter((app) => app.slug === appSlug)[0];

    const appLocation = foundApp?.appData?.location;

    if (!foundApp || !appLocation) {
      throw new BadRequestException(`${appSlug} not connected.`);
    }
    return foundApp.credential;
  }

  async disconnectConferencingApp(user: UserWithProfile, app: string) {
    const credential = await this.checkAppIsValidAndConnected(user, app);
    return handleDeleteCredential({
      userId: user.id,
      userMetadata: user?.metadata,
      credentialId: credential.id,
    });
  }

  async setDefaultConferencingApp(user: UserWithProfile, app: string) {
    // cal-video is global, so we can skip this check
    if (app !== CAL_VIDEO) {
      await this.checkAppIsValidAndConnected(user, app);
    }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Refresh the installed-apps list via GET /v2/conferencing before offering disconnect/set-default actions.
  2. If the user expects it connected, have them reconnect via the OAuth flow.
  3. For setDefaultConferencingApp, cal_video bypasses this check — use cal_video if no app is connected.
  4. Investigate any credential rows missing appData.location as a data-integrity bug.

Example fix

// before
await api.setDefaultConferencing('zoom');

// after
const installed = await api.listConferencingApps();
const slugs = installed.data.map(a => a.slug);
if (!slugs.includes('zoom')) {
  showToast('Connect Zoom first.'); return;
}
await api.setDefaultConferencing('zoom');
Defensive patterns

Strategy: validation

Validate before calling

async function isAppConnected(conferencingService: ConferencingService, user: UserWithProfile, appSlug: string): Promise<boolean> {
  try {
    await conferencingService.checkAppIsValidAndConnected(user, appSlug);
    return true;
  } catch (e) {
    if (e instanceof BadRequestException && /not connected/.test(e.message)) return false;
    throw e;
  }
}

if (!(await isAppConnected(conferencingService, user, app))) {
  return { code: 'app_not_connected', app };
}

Type guard

interface InstalledApp { slug: string; credential?: { id: number } }

function isConnectedApp<T extends InstalledApp>(apps: T[], slug: string): apps is (T & { credential: { id: number } })[] {
  return apps.some(a => a.slug === slug && Boolean(a.credential));
}

Try / catch

try {
  await conferencingService.setDefaultConferencingApp(user, app);
} catch (e) {
  if (e instanceof BadRequestException && /not connected/.test(e.message)) {
    return res.status(400).json({ code: 'app_not_connected', app });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling DELETE /v2/conferencing/zoom/disconnect when the user never connected Zoom, or connected then deleted it; calling POST /v2/conferencing/zoom/default without a Zoom credential; the credential exists but its appData.location is missing (incomplete install).

Common situations: Stale UI showing an app as connected; user connected in a different account/tenant; app-store credential row missing location metadata after a partial install.

Related errors


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