{"record":{"id":"8ae19fb27dfe555d","repo":"calcom/cal.diy","slug":"credentials-for-apple-calendar-not-found","errorCode":null,"errorMessage":"Credentials for apple calendar not found.","messagePattern":"Credentials for apple calendar not found\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts","lineNumber":39,"sourceCode":"    userEmail: string,\n    username: string,\n    password: string\n  ): Promise<{ status: string }> {\n    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 {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/calendars/services/apple-calendar.service.ts#L21-L57","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call the Apple Calendar 'save' endpoint (POST /v2/calendars/apple_calendar/save) with a valid username + app-specific password first, then call 'check'.","Verify the credential row exists: SELECT id, \"invalid\" FROM \"Credential\" WHERE type='apple_calendar' AND \"userId\"=<id>.","If the row was deleted unintentionally, re-run save; if it exists but invalid=true, see error 361."],"exampleFix":"// before: calling check on a fresh user\nawait api.get('/v2/calendars/apple_calendar/check'); // 400\n\n// after: save first, then check\nawait api.post('/v2/calendars/apple_calendar/save', { username, password });\nawait api.get('/v2/calendars/apple_calendar/check'); // 200","handlingStrategy":"validation","validationCode":"// Run before calling /v2/calendars/apple_calendar/check\nasync function hasAppleCredential(api, userId) {\n  const { data } = await api.get('/v2/calendars');\n  return data.connectedCalendars.some(c => c.integration?.type === 'apple_calendar');\n}\nif (!(await hasAppleCredential(api, userId))) {\n  throw new ClientError('Connect Apple Calendar (save) before checking.');\n}","typeGuard":"// Narrow a connected-calendars payload to the apple entry\nfunction isAppleConnected(list) {\n  return Array.isArray(list) && list.some(\n    (c) => c?.integration?.type === 'apple_calendar' && !c?.error\n  );\n}","tryCatchPattern":"try {\n  await api.get('/v2/calendars/apple_calendar/check');\n} catch (e) {\n  if (e.response?.status === 400 && /not found/i.test(e.response?.data?.message)) {\n    // Not connected yet — route the user to the save flow rather than retrying.\n    return redirectToAppleSave();\n  }\n  throw e;\n}","preventionTips":["Always call 'save' before 'check' in onboarding flows.","Treat a 400 'Credentials ... not found' as a connect prompt, not a transient retry.","Seed test users with an apple_calendar Credential row when writing e2e tests."],"tags":["apple-calendar","credentials","nestjs","onboarding","http-400"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}