calcom/cal.diy · error · BadRequestException
Invalid office 365 calendar credentials.
Error message
Invalid office 365 calendar credentials.
What it means
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.
Source
Thrown at apps/api/v2/src/platform/calendars/services/outlook.service.ts:99
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 {
status: SUCCESS_STATUS,
};
}
View on GitHub (pinned to 176037d0af)
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.
Defensive patterns
Strategy: validation
Validate before calling
const cred = await credentialRepository.findCredentialByTypeAndUserId('office365_calendar', userId);
if (cred?.invalid) {
// prompt reconnect rather than calling /check
} Try / catch
try {
await outlookService.checkIfCalendarConnected(userId);
} catch (e) {
if (e instanceof BadRequestException && e.message === 'Invalid office 365 calendar credentials.') {
// re-run the Office 365 connect flow
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- Credentials for office_365_calendar not found.
- Office 365 calendar not connected.
- ${office365Calendar.error?.message}
- Invalid Access token.
- Credentials for stripe not found.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/198d171387188430.
Report an issue: GitHub.