{"record":{"id":"c964a41143dfd110","repo":"calcom/cal.diy","slug":"credentials-for-office-365-calendar-not-found","errorCode":null,"errorMessage":"Credentials for office_365_calendar not found.","messagePattern":"Credentials for office_365_calendar not found\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/outlook.service.ts","lineNumber":95,"sourceCode":"      redirect_uri: this.redirectUri,\n      state: JSON.stringify(state),\n    };\n\n    const query = stringify(params);\n\n    const url = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?${query}`;\n\n    return url;\n  }\n\n  async checkIfCalendarConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {\n    const office365CalendarCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(\n      \"office365_calendar\",\n      userId\n    );\n\n    if (!office365CalendarCredentials) {\n      throw new BadRequestException(\"Credentials for office_365_calendar not found.\");\n    }\n\n    if (office365CalendarCredentials.invalid) {\n      throw new BadRequestException(\"Invalid office 365 calendar credentials.\");\n    }\n\n    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);\n    const office365Calendar = connectedCalendars.find(\n      (cal: { integration: { type: string } }) => cal.integration.type === OFFICE_365_CALENDAR_TYPE\n    );\n    if (!office365Calendar) {\n      throw new UnauthorizedException(\"Office 365 calendar not connected.\");\n    }\n    if (office365Calendar.error?.message) {\n      throw new UnauthorizedException(office365Calendar.error?.message);\n    }\n\n    return {","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/outlook.service.ts#L77-L113","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Run the Office 365 connect flow (saveCalendarCredentialsAndRedirect) first so a credential row of type 'office365_calendar' is created.","Confirm the userId sent to /check is the numeric id of the user who completed connect.","Treat a 400 from /check as 'not configured' on the client and prompt the user to connect."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const cred = await credentialRepository.findCredentialByTypeAndUserId('office365_calendar', userId);\nif (!cred) {\n  return { status: 'not_configured' }; // prompt user to connect Office 365\n}","typeGuard":null,"tryCatchPattern":"try {\n  await outlookService.checkIfCalendarConnected(userId);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message.includes('not found')) {\n    // trigger the Office 365 connect flow\n  } else throw e;\n}","preventionTips":["Run the Office 365 connect flow before calling /check.","Pass the exact userId that completed connect.","Treat a 400 'not found' as 'not configured', not a transient error."],"tags":["outlook","office365","calendar","precondition","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}