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
- Detect the existing account and sign the user in (link the social ID) instead of creating a new account
- Handle AccountAlreadyExists in the signup flow by fetching the existing account by the social key
- 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
- Always attempt social-key lookup before account creation
- Make signup flows idempotent for repeated grants with the same email
- Handle provider-side email changes by re-linking, not re-creating
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.