calcom/cal.diy · error · BadRequestException

Credentials for office_365_calendar not found.

Error message

Credentials for office_365_calendar not found.

What it means

Thrown by OutlookService.checkIfCalendarConnected. It looks up the user's credential row of type 'office365_calendar' via credentialRepository.findCredentialByTypeAndUserId('office365_calendar', userId). If no such row exists, BadRequestException is raised: the user has never connected an Office 365 calendar, so the connectivity check has nothing to evaluate. This mirrors the structure of the Google and ICS check methods.

Source

Thrown at apps/api/v2/src/platform/calendars/services/outlook.service.ts:95

      redirect_uri: this.redirectUri,
      state: JSON.stringify(state),
    };

    const query = stringify(params);

    const url = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${query}`;

    return url;
  }

  async checkIfCalendarConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {
    const office365CalendarCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(
      "office365_calendar",
      userId
    );

    if (!office365CalendarCredentials) {
      throw new BadRequestException("Credentials for office_365_calendar not found.");
    }

    if (office365CalendarCredentials.invalid) {
      throw new BadRequestException("Invalid office 365 calendar credentials.");
    }

    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);
    const office365Calendar = connectedCalendars.find(
      (cal: { integration: { type: string } }) => cal.integration.type === OFFICE_365_CALENDAR_TYPE
    );
    if (!office365Calendar) {
      throw new UnauthorizedException("Office 365 calendar not connected.");
    }
    if (office365Calendar.error?.message) {
      throw new UnauthorizedException(office365Calendar.error?.message);
    }

    return {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Run the Office 365 connect flow (saveCalendarCredentialsAndRedirect) first so a credential row of type 'office365_calendar' is created.
  2. Confirm the userId sent to /check is the numeric id of the user who completed connect.
  3. Treat a 400 from /check as 'not configured' on the client and prompt the user to connect.
Defensive patterns

Strategy: validation

Validate before calling

const cred = await credentialRepository.findCredentialByTypeAndUserId('office365_calendar', userId);
if (!cred) {
  return { status: 'not_configured' }; // prompt user to connect Office 365
}

Try / catch

try {
  await outlookService.checkIfCalendarConnected(userId);
} catch (e) {
  if (e instanceof BadRequestException && e.message.includes('not found')) {
    // trigger the Office 365 connect flow
  } else throw e;
}

Prevention

When it happens

Trigger: Calling GET /v2/calendars/office365/check for a user who never completed the Office 365 OAuth connect flow; passing the wrong userId; the credential was deleted from the webapp's integrations page; the type literal 'office365_calendar' is mismatched against what was actually saved (e.g. a legacy 'outlook' type).

Common situations: Frontend polls /check before /connect completes; test/dev environment mismatch; a user removed the Office 365 integration and the client keeps polling; account switch where the new user has no O365 credential.

Related errors


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