{"record":{"id":"198d171387188430","repo":"calcom/cal.diy","slug":"invalid-office-365-calendar-credentials","errorCode":null,"errorMessage":"Invalid office 365 calendar credentials.","messagePattern":"Invalid office 365 calendar credentials\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/outlook.service.ts","lineNumber":99,"sourceCode":"    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 {\n      status: SUCCESS_STATUS,\n    };\n  }\n","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/outlook.service.ts#L81-L117","documentation":"In OutlookService.checkIfCalendarConnected, after the office365_calendar credential is found it is checked for the `invalid` flag. Cal.com flips this flag when a prior Microsoft Graph call failed (revoked refresh token, consent withdrawn, deleted mailbox). A truthy invalid short-circuits with BadRequestException('Invalid office 365 calendar credentials.') rather than attempting the live getCalendars enumeration.","triggerScenarios":"User revoked app consent in the Microsoft account (https://account.activedirectory.windowsazure.com); the refresh token expired (90-day inactivity) and a refresh attempt failed enough times to mark invalid; admin removed the enterprise app; Graph API permission scopes were reduced so existing tokens no longer work.","commonSituations":"Long-dormant O365 integration; tenant admin revoked the app registration; consent was granted for personal account then user switched to a work account; scopes mismatch between the app registration and what Cal requests (offline_access, Calendars.Read, Calendars.ReadWrite per getOAuthCredentials).","solutions":["Re-run the Office 365 connect flow; a fresh OAuth code will replace the credential and clear invalid.","In the Microsoft account, confirm the Cal app registration still has consent and the required delegated scopes (offline_access, Calendars.Read, Calendars.ReadWrite).","If this is an enterprise tenant, verify the admin hasn't disabled or removed the app registration."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const cred = await credentialRepository.findCredentialByTypeAndUserId('office365_calendar', userId);\nif (cred?.invalid) {\n  // prompt reconnect rather than calling /check\n}","typeGuard":null,"tryCatchPattern":"try {\n  await outlookService.checkIfCalendarConnected(userId);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message === 'Invalid office 365 calendar credentials.') {\n    // re-run the Office 365 connect flow\n  } else throw e;\n}","preventionTips":["Reconnect Office 365 whenever consent is revoked or scopes change.","Keep the Microsoft app registration's delegated scopes at offline_access, Calendars.Read, Calendars.ReadWrite.","Surface 'invalid' state in the UI so users reconnect proactively."],"tags":["outlook","office365","credentials","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}