{"record":{"id":"006a36a66d09ff1d","repo":"calcom/cal.diy","slug":"already-installed-006a36","errorCode":null,"errorMessage":"Already installed","messagePattern":"Already installed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/app-store/alby/api/add.ts","lineNumber":21,"sourceCode":"import { getServerErrorFromUnknown } from \"@calcom/lib/server/getServerErrorFromUnknown\";\nimport prisma from \"@calcom/prisma\";\n\nimport config from \"../config.json\";\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  if (!req.session?.user?.id) {\n    return res.status(401).json({ message: \"You must be logged in to do this\" });\n  }\n  const appType = config.type;\n  try {\n    const alreadyInstalled = await prisma.credential.findFirst({\n      where: {\n        type: appType,\n        userId: req.session.user.id,\n      },\n    });\n    if (alreadyInstalled) {\n      throw new Error(\"Already installed\");\n    }\n    const installation = await prisma.credential.create({\n      data: {\n        type: appType,\n        key: {},\n        userId: req.session.user.id,\n        appId: \"alby\",\n      },\n    });\n\n    if (!installation) {\n      throw new Error(\"Unable to create user credential for Alby\");\n    }\n  } catch (error: unknown) {\n    const httpError = getServerErrorFromUnknown(error);\n    return res.status(httpError.statusCode).json({ message: httpError.message });\n  }\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/packages/app-store/alby/api/add.ts#L3-L39","documentation":"Thrown from Alby's 'add' handler when a credential row with the Alby app type already exists for the current user. Alby supports only one credential per user, so re-running the install flow is rejected. The throw is caught by the surrounding try/catch and converted by getServerErrorFromUnknown into the HTTP response.","triggerScenarios":"User clicks 'Install Alby' a second time while a credential row already exists; the install flow was interrupted and re-triggered; a previous uninstall left the credential behind.","commonSituations":"Stale credential after a failed uninstall; dev seed data with a pre-existing alby credential; double-click on the install button; race where two install requests fire concurrently.","solutions":["Uninstall the existing Alby credential first, then retry the install.","Detect the existing credential on the client and route the user straight to /apps/alby/setup.","In dev, remove the row: prisma.credential.deleteMany({ where: { type: <appType>, userId } }).","Treat the 'Already installed' message in the UI as a redirect, not a hard error."],"exampleFix":"// before\nif (alreadyInstalled) {\n  throw new Error('Already installed');\n}\n\n// after\nif (alreadyInstalled) {\n  return res.status(200).json({ url: '/apps/alby/setup' });\n}","handlingStrategy":"validation","validationCode":"const existing = await prisma.credential.findFirst({\n  where: { type: appType, userId },\n});\nif (existing) {\n  // route to setup instead of re-running add\n}","typeGuard":"const isAlreadyInstalledError = (e: unknown) =>\n  e instanceof Error && e.message === 'Already installed';","tryCatchPattern":"try {\n  await fetch('/api/integrations/alby/add').then((r) => r.json());\n} catch (e) {\n  if (isAlreadyInstalledError(e)) {\n    router.push('/apps/alby/setup');\n  } else throw e;\n}","preventionTips":["Check for an existing credential before offering the install button.","Treat 'Already installed' as a redirect, not a failure.","Idempotently delete the credential before re-installing in dev."],"tags":["alby","duplicate","credential","integration"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}