{"record":{"id":"31b38eae4935ece5","repo":"calcom/cal.diy","slug":"invalid-apple-calendar-credentials","errorCode":null,"errorMessage":"Invalid apple calendar credentials.","messagePattern":"Invalid apple calendar credentials\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts","lineNumber":43,"sourceCode":"    return await this.saveCalendarCredentials(userId, userEmail, username, password);\n  }\n\n  async check(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {\n    return await this.checkIfCalendarConnected(userId);\n  }\n\n  async checkIfCalendarConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {\n    const appleCalendarCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(\n      APPLE_CALENDAR_TYPE,\n      userId\n    );\n\n    if (!appleCalendarCredentials) {\n      throw new BadRequestException(\"Credentials for apple calendar not found.\");\n    }\n\n    if (appleCalendarCredentials.invalid) {\n      throw new BadRequestException(\"Invalid apple calendar credentials.\");\n    }\n\n    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);\n    const appleCalendar = connectedCalendars.find(\n      (cal: { integration: { type: string } }) => cal.integration.type === APPLE_CALENDAR_TYPE\n    );\n    if (!appleCalendar) {\n      throw new UnauthorizedException(\"Apple calendar not connected.\");\n    }\n    if (appleCalendar.error?.message) {\n      throw new UnauthorizedException(appleCalendar.error?.message);\n    }\n\n    return {\n      status: SUCCESS_STATUS,\n    };\n  }\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"User changed their Apple ID password; app-specific password revoked at appleid.apple.com; long-dormant credential that failed a background refresh.","solutions":["Re-save the credential via the 'save' endpoint — saveCalendarCredentials upserts and overwrites the invalid row (see lines 97-108 for the update path).","Have the user generate a fresh app-specific password at appleid.apple.com and submit it via save.","Confirm the 'invalid' column is reset to false after a successful save before retrying check."],"exampleFix":"// before: re-checking an invalid credential\nawait api.get('/v2/calendars/apple_calendar/check'); // 400 'Invalid apple calendar credentials.'\n\n// after: overwrite with fresh app-specific password\nawait api.post('/v2/calendars/apple_calendar/save', {\n  username: userAppleId,\n  password: newlyGeneratedAppSpecificPassword,\n});","handlingStrategy":"validation","validationCode":"// Check the invalid flag via the calendars list before calling check\nconst { data } = await api.get('/v2/calendars');\nconst apple = data.connectedCalendars.find(c => c.integration?.type === 'apple_calendar');\nif (apple?.error || apple === undefined) {\n  // credential missing or invalid — prompt re-save\n  await promptAppleReSave();\n}","typeGuard":"function isCredentialValid(cred) {\n  return !!cred && cred.invalid === false;\n}","tryCatchPattern":"try {\n  await api.get('/v2/calendars/apple_calendar/check');\n} catch (e) {\n  if (e.response?.status === 400 && /invalid/i.test(e.response?.data?.message)) {\n    await reSaveAppleCredentials(); // overwrite the invalid row\n    return;\n  }\n  throw e;\n}","preventionTips":["When a listCalendars/DAV call fails, mark invalid and prompt the user to re-save rather than silently retrying.","App-specific passwords should be regenerated at appleid.apple.com when rotated.","After a successful save, assert the invalid flag is false before proceeding."],"tags":["apple-calendar","credentials","invalid-flag","auth","http-400"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}