hcengineering/platform · error · PlatformError

AccountAlreadyExists

AccountAlreadyExists

Error message

AccountAlreadyExists

What it means

AccountAlreadyExists is thrown during social sign-up/account creation when an account for the email's social ID already exists in the database. The lookup by emailSocialId.personUuid finds a non-null account, so creating a new one would duplicate it.

Source

Thrown at server/account/src/utils.ts:696

  email: string,
  password: string | null,
  firstName: string,
  lastName: string,
  confirmed = false,
  automatic = false
): Promise<{ account: AccountUuid, socialId: PersonId }> {
  const normalizedEmail = cleanEmail(email)

  const emailSocialId = await getEmailSocialId(db, normalizedEmail)
  let account: AccountUuid
  let socialId: PersonId

  if (emailSocialId !== null) {
    const existingAccount = await db.account.findOne({ uuid: emailSocialId.personUuid as AccountUuid })

    if (existingAccount !== null) {
      ctx.error('An account with the provided email already exists', { email })
      throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountAlreadyExists, {}))
    }

    account = emailSocialId.personUuid as AccountUuid
    socialId = emailSocialId._id
    // Person exists, but may have different name, need to update with what's been provided
    await db.person.update({ uuid: account }, { firstName, lastName })
  } else {
    // There's no person we can link to this email, so we need to create a new one
    account = await db.person.insertOne({ firstName, lastName })
    socialId = await db.socialId.insertOne({
      type: SocialIdType.EMAIL,
      value: normalizedEmail,
      personUuid: account,
      ...(confirmed ? { verifiedOn: Date.now() } : {})
    })
  }

  await createAccount(db, account, confirmed, automatic)

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Detect the existing account and sign the user in (link the social ID) instead of creating a new account
  2. Handle AccountAlreadyExists in the signup flow by fetching the existing account by the social key
  3. Deduplicate at the caller level: check for an existing person by social key before invoking createAccount

Example fix

// before
const account = await createAccountWithSocialId(ctx, db, identity, email, firstName, lastName)
// after
let account = await findPersonBySocialKey(ctx, token, { socialString: email })
if (account == null) account = await createAccountWithSocialId(ctx, db, identity, email, firstName, lastName)
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = await findPersonBySocialKey(ctx, token, { socialString: email })
if (existing != null) return existing // sign in instead of sign up

Type guard

function accountExists(a: unknown): a is { uuid: AccountUuid } { return a != null && typeof (a as any).uuid === 'string' }

Try / catch

try { account = await createAccountWithSocialId(ctx, db, identity, email, firstName, lastName) } catch (err) { if (isStatus(err, platform.status.AccountAlreadyExists)) { account = await findPersonBySocialKey(ctx, token, { socialString: email }); } else throw err }

Prevention

When it happens

Trigger: Signing up with a social grant (e.g. Google/GitHub) whose email is already linked to an existing account (server/account/src/utils.ts:696).

Common situations: A user signs up twice with the same email via social login; email change on the provider caused a re-link attempt; migrating users from another identity provider creates duplicate provisioning calls.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/66377b195e463fb5. Report an issue: GitHub.