hcengineering/platform · error · PlatformError
IntegrationAlreadyExists
IntegrationAlreadyExists
Error message
IntegrationAlreadyExists
What it means
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.
Source
Thrown at server/account/src/serviceOperations.ts:668
if (socialId != null) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.SocialIdNotFound, { _id: personId }))
}
await db.socialId.update({ _id: personId }, { displayValue })
}
export async function createIntegration (
ctx: MeasureContext,
db: AccountDB,
branding: Branding | null,
token: string,
params: Integration
): Promise<void> {
const { extra, account } = decodeTokenVerbose(ctx, token)
// it checks params and throws BadRequest if params are invalid
const existing = await findExistingIntegration(account, db, params, extra)
if (existing != null) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationAlreadyExists, {}))
}
const { socialId, kind, workspaceUuid, data } = params
const social = await db.socialId.findOne({ _id: socialId })
if (social?.personUuid === readOnlyGuestAccountUuid) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
}
await db.integration.insertOne({ socialId, kind, workspaceUuid, data })
}
export async function updateIntegration (
ctx: MeasureContext,
db: AccountDB,
branding: Branding | null,
token: string,
params: IntegrationView on GitHub (pinned to 63e28dc964)
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.
Example fix
// before
await client.createIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })
// after
const existing = await client.getIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid })
if (existing == null) {
await client.createIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })
} else {
await client.updateIntegration(ctx, token, { socialId, kind: 'github', workspaceUuid, data })
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await client.getIntegration(ctx, token, { socialId, kind, workspaceUuid })
if (existing != null) throw new Error('integration already exists; call updateIntegration instead') Type guard
function canCreate(p: Integration): boolean {
return p.kind != null && p.kind !== '' && p.socialId != null && p.socialId !== '' && p.workspaceUuid !== undefined
} Try / catch
try {
await client.createIntegration(ctx, token, params)
} catch (err) {
if (err instanceof PlatformError && err.status.code === platform.status.IntegrationAlreadyExists) {
return // idempotent: already present
}
throw err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Integration not found: ${JSON.stringify(integrationKey)}
- Email is not defined in integration
- Accounts URL or token is not defined
- IntegrationNotFound
- IntegrationSecretAlreadyExists
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/2e1eba26ac4bcdfe.
Report an issue: GitHub.