calcom/cal.diy · error · UnauthorizedException
Office 365 calendar not connected.
Error message
Office 365 calendar not connected.
What it means
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).
Source
Thrown at apps/api/v2/src/platform/calendars/services/outlook.service.ts:107
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,
};
}
async getOAuthCredentials(code: string) {
const scopes = ["offline_access", "Calendars.Read", "Calendars.ReadWrite"];
const { client_id, client_secret } = await this.calendarsService.getAppKeys(OFFICE_365_CALENDAR_ID);
const toUrlEncoded = (payload: Record<string, string>) =>
Object.keys(payload)
.map((key) => `${key}=${encodeURIComponent(payload[key])}`)
.join("&");View on GitHub (pinned to 176037d0af)
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.
Defensive patterns
Strategy: validation
Validate before calling
const { connectedCalendars } = await calendarsService.getCalendars(userId);
const linked = connectedCalendars.some(c => c.integration.type === OFFICE_365_CALENDAR_TYPE);
if (!linked) {
// re-run connect to recreate the calendar linkage
} Try / catch
try {
await outlookService.checkIfCalendarConnected(userId);
} catch (e) {
if (e instanceof UnauthorizedException && e.message === 'Office 365 calendar not connected.') {
// re-run connect flow
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Credentials for office_365_calendar not found.
- Invalid office 365 calendar credentials.
- ${office365Calendar.error?.message}
- Invalid Access token.
- Missing `state` query param
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/bb0ac6e89008f567.
Report an issue: GitHub.