calcom/cal.diy · error · BadRequestException

Credentials for apple calendar not found.

Error message

Credentials for apple calendar not found.

What it means

Thrown by AppleCalendarService.checkIfCalendarConnected (apple-calendar.service.ts:39) as a NestJS BadRequestException (HTTP 400) when findCredentialByTypeAndUserId(APPLE_CALENDAR_TYPE, userId) returns null — no Apple Calendar credential row exists for the user. Reached via the calendars 'check' endpoint (calendar = apple_calendar). Note the semantic mismatch: a 'not found' condition is reported as 400 rather than 404.

Source

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

    userEmail: string,
    username: string,
    password: string
  ): Promise<{ status: string }> {
    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 {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Call the Apple Calendar 'save' endpoint (POST /v2/calendars/apple_calendar/save) with a valid username + app-specific password first, then call 'check'.
  2. Verify the credential row exists: SELECT id, "invalid" FROM "Credential" WHERE type='apple_calendar' AND "userId"=<id>.
  3. If the row was deleted unintentionally, re-run save; if it exists but invalid=true, see error 361.

Example fix

// before: calling check on a fresh user
await api.get('/v2/calendars/apple_calendar/check'); // 400

// after: save first, then check
await api.post('/v2/calendars/apple_calendar/save', { username, password });
await api.get('/v2/calendars/apple_calendar/check'); // 200
Defensive patterns

Strategy: validation

Validate before calling

// Run before calling /v2/calendars/apple_calendar/check
async function hasAppleCredential(api, userId) {
  const { data } = await api.get('/v2/calendars');
  return data.connectedCalendars.some(c => c.integration?.type === 'apple_calendar');
}
if (!(await hasAppleCredential(api, userId))) {
  throw new ClientError('Connect Apple Calendar (save) before checking.');
}

Type guard

// Narrow a connected-calendars payload to the apple entry
function isAppleConnected(list) {
  return Array.isArray(list) && list.some(
    (c) => c?.integration?.type === 'apple_calendar' && !c?.error
  );
}

Try / catch

try {
  await api.get('/v2/calendars/apple_calendar/check');
} catch (e) {
  if (e.response?.status === 400 && /not found/i.test(e.response?.data?.message)) {
    // Not connected yet — route the user to the save flow rather than retrying.
    return redirectToAppleSave();
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling GET/POST /v2/calendars/apple_calendar/check for a user who never completed the 'save' step; calling check after the credential was disconnected/deleted; a seeded test user with no apple_calendar Credential row.

Common situations: Onboarding UI polling 'check' before 'save' finishes; test fixtures that create a User but omit Credentials; a previously disconnected calendar whose status is still being polled.

Related errors


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