calcom/cal.diy · error · UnauthorizedException

Apple calendar not connected.

Error message

Apple calendar not connected.

What it means

Thrown by AppleCalendarService.checkIfCalendarConnected (apple-calendar.service.ts:51) as UnauthorizedException (HTTP 401) when the credential row exists and is valid, but calendarsService.getCalendars(userId) returned a connectedCalendars array containing no entry whose integration.type === APPLE_CALENDAR_TYPE. The credential is stored but no Apple calendar is actually surfaced/linked by the integration layer.

Source

Thrown at apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts:51

    const appleCalendarCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(
      APPLE_CALENDAR_TYPE,
      userId
    );

    if (!appleCalendarCredentials) {
      throw new BadRequestException("Credentials for apple calendar not found.");
    }

    if (appleCalendarCredentials.invalid) {
      throw new BadRequestException("Invalid apple calendar credentials.");
    }

    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);
    const appleCalendar = connectedCalendars.find(
      (cal: { integration: { type: string } }) => cal.integration.type === APPLE_CALENDAR_TYPE
    );
    if (!appleCalendar) {
      throw new UnauthorizedException("Apple calendar not connected.");
    }
    if (appleCalendar.error?.message) {
      throw new UnauthorizedException(appleCalendar.error?.message);
    }

    return {
      status: SUCCESS_STATUS,
    };
  }

  async saveCalendarCredentials(userId: number, userEmail: string, username: string, password: string) {
    if (!username || !password || username.length <= 1 || password.length <= 1) {
      throw new BadRequestException(`Username or password cannot be empty`);
    }

    const existingAppleCalendarCredentials = await this.credentialRepository.getAllUserCredentialsByTypeAndId(
      APPLE_CALENDAR_TYPE,
      userId

View on GitHub (pinned to 176037d0af)

Solutions

  1. Invalidate the connected-calendars cache for the user (calendarsCacheService.deleteConnectedAndDestinationCalendarsCache) and retry check.
  2. Confirm a SelectedCalendars row exists for the apple calendar externalId + credentialId.
  3. Re-run the save flow, which calls dav.listCalendars() and upserts the credential, forcing the integration to re-link.

Example fix

// before: check immediately after save returns 401 due to stale cache
await api.post('/v2/calendars/apple_calendar/save', { ... });
await api.get('/v2/calendars/apple_calendar/check'); // 401 'Apple calendar not connected.'

// after: clear cache (server-side) or wait for invalidation, then check
calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(userId);
await api.get('/v2/calendars/apple_calendar/check'); // 200
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the apple calendar appears in the (cache-busted) list before checking
await api.post('/v2/calendars/apple_calendar/save', { username, password });
// server should invalidate cache; if not, force a refresh path that bypasses cache
const { data } = await api.get('/v2/calendars', { params: { ensureDefaultSelectedCalendars: true } });
const linked = data.connectedCalendars.some(c => c.integration?.type === 'apple_calendar');
if (!linked) throw new Error('Apple calendar did not link — see save logs');

Type guard

function hasConnectedApple(list) {
  return Array.isArray(list) && list.some(
    (c) => c?.integration?.type === 'apple_calendar'
  );
}

Try / catch

try {
  await api.get('/v2/calendars/apple_calendar/check');
} catch (e) {
  if (e.response?.status === 401 && /not connected/i.test(e.response?.data?.message)) {
    // likely stale cache or incomplete link — re-save then retry once
    await api.post('/v2/calendars/apple_calendar/save', { username, password });
    return api.get('/v2/calendars/apple_calendar/check');
  }
  throw e;
}

Prevention

When it happens

Trigger: Credential saved but the link/selectedCalendars step never completed; CalendarsCacheService returns a stale cached list missing the apple entry (cache not invalidated after save); the integration returned calendars but none matched the type filter.

Common situations: Race between an in-flight save and a check; cache key stale because save didn't call deleteConnectedAndDestinationCalendarsCache; selectedCalendars row missing for the apple calendar.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/c4ce1e51bc285639. Report an issue: GitHub.