{"record":{"id":"2e1eba26ac4bcdfe","repo":"hcengineering/platform","slug":"integrationalreadyexists","errorCode":"IntegrationAlreadyExists","errorMessage":"IntegrationAlreadyExists","messagePattern":"IntegrationAlreadyExists","errorType":"exception","errorClass":"PlatformError","httpStatus":null,"severity":"error","filePath":"server/account/src/serviceOperations.ts","lineNumber":668,"sourceCode":"  if (socialId != null) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.SocialIdNotFound, { _id: personId }))\n  }\n\n  await db.socialId.update({ _id: personId }, { displayValue })\n}\n\nexport async function createIntegration (\n  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string,\n  params: Integration\n): Promise<void> {\n  const { extra, account } = decodeTokenVerbose(ctx, token)\n  // it checks params and throws BadRequest if params are invalid\n  const existing = await findExistingIntegration(account, db, params, extra)\n  if (existing != null) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationAlreadyExists, {}))\n  }\n\n  const { socialId, kind, workspaceUuid, data } = params\n  const social = await db.socialId.findOne({ _id: socialId })\n\n  if (social?.personUuid === readOnlyGuestAccountUuid) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))\n  }\n\n  await db.integration.insertOne({ socialId, kind, workspaceUuid, data })\n}\n\nexport async function updateIntegration (\n  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string,\n  params: Integration","sourceCodeStart":650,"sourceCodeEnd":686,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server/account/src/serviceOperations.ts#L650-L686","documentation":"PlatformStatus IntegrationAlreadyExists is thrown by the account service's createIntegration when an integration with the exact (socialId, kind, workspaceUuid) key already exists in the integration collection. findExistingIntegration is consulted first, and any non-null hit means the caller is attempting to insert a duplicate integration. It is a logical/ pre-condition violation of the unique key, not a transport or auth failure.","triggerScenarios":"Calling createIntegration with params whose (socialId, kind, workspaceUuid) tuple matches a row already inserted by a previous createIntegration call (e.g. re-running an OAuth connect flow for the same GitHub/Gmail account in the same workspace).","commonSituations":"Retry logic that blindly re-invokes createIntegration after a timeout; client double-submit of the connect dialog; re-connecting a social account that was already integrated rather than calling updateIntegration to change its data.","solutions":["Check for the existing integration first (via getIntegration or a prior listIntegrations) and call updateIntegration instead when one exists.","Catch PlatformError with status IntegrationAlreadyExists and treat it as idempotent success if the stored data matches the request.","Delete the stale integration via deleteIntegration (or use upsert-style logic client-side) before recreating it.","Ensure the client generates distinct kind values when the same socialId is intentionally used for multiple integrations of different types."],"exampleFix":"// before\nawait client.createIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })\n// after\nconst existing = await client.getIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid })\nif (existing == null) {\n  await client.createIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })\n} else {\n  await client.updateIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })\n}","handlingStrategy":"try-catch","validationCode":"const existing = await client.getIntegration(ctx, token, { socialId, kind, workspaceUuid })\nif (existing != null) throw new Error('integration already exists; call updateIntegration instead')","typeGuard":"function canCreate(p: Integration): boolean {\n  return p.kind != null && p.kind !== '' && p.socialId != null && p.socialId !== '' && p.workspaceUuid !== undefined\n}","tryCatchPattern":"try {\n  await client.createIntegration(ctx, token, params)\n} catch (err) {\n  if (err instanceof PlatformError && err.status.code === platform.status.IntegrationAlreadyExists) {\n    return // idempotent: already present\n  }\n  throw err\n}","preventionTips":["Check existence with getIntegration before createIntegration","Implement client-side upsert (create-if-missing, update-if-present)","Debounce/deduplicate connect-flow submissions in the UI","Use consistent kind values per socialId to avoid accidental collisions"],"tags":["duplicate-key","integration","idempotency"],"backgroundTag":"duplicate-record-conflict","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}