calcom/cal.diy · error · BadRequestException

Invalid apple calendar credentials.

Error message

Invalid apple calendar credentials.

What it means

Thrown by AppleCalendarService.checkIfCalendarConnected (apple-calendar.service.ts:43) as BadRequestException (HTTP 400) when the Apple Calendar credential row exists but its 'invalid' column is true. The flag is set by upstream code when a prior DAV authentication or listCalendars call failed, marking the stored username/password as no longer working.

Source

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

    return await this.saveCalendarCredentials(userId, userEmail, username, password);
  }

  async check(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {
    return await this.checkIfCalendarConnected(userId);
  }

  async checkIfCalendarConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {
    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,
    };
  }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Re-save the credential via the 'save' endpoint — saveCalendarCredentials upserts and overwrites the invalid row (see lines 97-108 for the update path).
  2. Have the user generate a fresh app-specific password at appleid.apple.com and submit it via save.
  3. Confirm the 'invalid' column is reset to false after a successful save before retrying check.

Example fix

// before: re-checking an invalid credential
await api.get('/v2/calendars/apple_calendar/check'); // 400 'Invalid apple calendar credentials.'

// after: overwrite with fresh app-specific password
await api.post('/v2/calendars/apple_calendar/save', {
  username: userAppleId,
  password: newlyGeneratedAppSpecificPassword,
});
Defensive patterns

Strategy: validation

Validate before calling

// Check the invalid flag via the calendars list before calling check
const { data } = await api.get('/v2/calendars');
const apple = data.connectedCalendars.find(c => c.integration?.type === 'apple_calendar');
if (apple?.error || apple === undefined) {
  // credential missing or invalid — prompt re-save
  await promptAppleReSave();
}

Type guard

function isCredentialValid(cred) {
  return !!cred && cred.invalid === false;
}

Try / catch

try {
  await api.get('/v2/calendars/apple_calendar/check');
} catch (e) {
  if (e.response?.status === 400 && /invalid/i.test(e.response?.data?.message)) {
    await reSaveAppleCredentials(); // overwrite the invalid row
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling 'check' after the credential was flagged invalid by a previous failed listCalendars/refresh; user rotated their Apple ID password or revoked the app-specific password; the credential row's invalid flag was set by an admin/migration.

Common situations: User changed their Apple ID password; app-specific password revoked at appleid.apple.com; long-dormant credential that failed a background refresh.

Related errors


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