{"record":{"id":"bb0ac6e89008f567","repo":"calcom/cal.diy","slug":"office-365-calendar-not-connected","errorCode":null,"errorMessage":"Office 365 calendar not connected.","messagePattern":"Office 365 calendar not connected\\.","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/outlook.service.ts","lineNumber":107,"sourceCode":"    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 {\n      status: SUCCESS_STATUS,\n    };\n  }\n\n  async getOAuthCredentials(code: string) {\n    const scopes = [\"offline_access\", \"Calendars.Read\", \"Calendars.ReadWrite\"];\n    const { client_id, client_secret } = await this.calendarsService.getAppKeys(OFFICE_365_CALENDAR_ID);\n\n    const toUrlEncoded = (payload: Record<string, string>) =>\n      Object.keys(payload)\n        .map((key) => `${key}=${encodeURIComponent(payload[key])}`)\n        .join(\"&\");","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/outlook.service.ts#L89-L125","documentation":"After the O365 credential is found and not invalid, OutlookService.checkIfCalendarConnected calls calendarsService.getCalendars(userId) and searches connectedCalendars for an entry whose integration.type === OFFICE_365_CALENDAR_TYPE. If none exists, the credential is present but the calendar isn't linked into the user's connected-calendars set, so UnauthorizedException('Office 365 calendar not connected.') is thrown (HTTP 401 because the state implies the session/identity isn't fully wired).","triggerScenarios":"A prior connect flow persisted the credential but failed before createAndLinkCalendarEntry completed; the linkage row was deleted; the connectedCalendars cache serves a pre-connect snapshot; the user changed account so the credential row exists but isn't linked to the calling user's calendar set.","commonSituations":"Partial save (credential upserted, calendar not linked); stale connected-calendars cache; cross-user or cross-team identity confusion; DB rollback that hit only the linking step.","solutions":["Re-run the Office 365 connect flow to re-execute createAndLinkCalendarEntry.","Force-bust the connected-calendars cache for the userId to rule out a stale read.","Confirm the calling user matches the user that owns the credential row."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const { connectedCalendars } = await calendarsService.getCalendars(userId);\nconst linked = connectedCalendars.some(c => c.integration.type === OFFICE_365_CALENDAR_TYPE);\nif (!linked) {\n  // re-run connect to recreate the calendar linkage\n}","typeGuard":null,"tryCatchPattern":"try {\n  await outlookService.checkIfCalendarConnected(userId);\n} catch (e) {\n  if (e instanceof UnauthorizedException && e.message === 'Office 365 calendar not connected.') {\n    // re-run connect flow\n  } else throw e;\n}","preventionTips":["If a connect partially fails, re-connect rather than calling /check.","Bust the connected-calendars cache after connect.","Keep the userId consistent between connect and check."],"tags":["outlook","office365","state","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}