hcengineering/platform · error · PlatformError
AccountAlreadyExists
AccountAlreadyExists
Error message
An account with the provided email already exists
What it means
In signUpOtp, the email social id already resolves to a person that has an existing account, so a new signup would duplicate it. The server throws AccountAlreadyExists instead of proceeding with account creation.
Source
Thrown at server/account/src/operations.ts:374
lastName?: string
}
): Promise<OtpInfo> {
const { email, firstName, lastName } = params
if (email == null || firstName == null || email === '' || firstName === '') {
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
}
// Note: can support OTP based on any other social logins later
const normalizedEmail = cleanEmail(email)
let emailSocialId = await getEmailSocialId(db, normalizedEmail)
let personUuid: PersonUuid
if (emailSocialId !== null) {
const existingAccount = await db.account.findOne({ uuid: emailSocialId.personUuid as AccountUuid })
if (existingAccount !== null) {
ctx.warn('An account with the provided email already exists', { email })
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountAlreadyExists, {}))
}
await db.person.update({ uuid: emailSocialId.personUuid }, { firstName, lastName: lastName ?? '' })
personUuid = emailSocialId.personUuid
} else {
// There's no person linked to this email, so we need to create a new one
personUuid = await db.person.insertOne({ firstName, lastName: lastName ?? '' })
const newSocialId = { type: SocialIdType.EMAIL, value: normalizedEmail, personUuid }
const emailSocialIdId = await db.socialId.insertOne(newSocialId)
emailSocialId = { ...newSocialId, _id: emailSocialIdId, key: buildSocialIdString(newSocialId) }
}
return await sendOtp(ctx, db, branding, emailSocialId)
}
/**View on GitHub (pinned to 63e28dc964)
Solutions
- Detect the AccountAlreadyExists status and route the user to the login / password-reset flow instead of signup.
- Have the user sign in with the original method (social login or password) tied to that email.
- If the account is truly orphaned, an admin can unlink or remove the duplicate account record.
- Disable the signup submit button while the request is in-flight to prevent double submission.
Example fix
// before
await client.signUpOtp(email, firstName, lastName) // throws on duplicate
// after
try {
await client.signUpOtp(email, firstName, lastName)
} catch (e) {
if (isStatusError(e, platform.status.AccountAlreadyExists)) {
redirectToLogin(email)
return
}
throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await lookupAccountByEmail(email) if (existing != null) redirectToLogin(email)
Type guard
function isAccountAlreadyExists(e: unknown): boolean {
return e instanceof PlatformError && e.status.code === platform.status.AccountAlreadyExists
} Try / catch
try {
await client.signUpOtp(email, firstName, lastName)
} catch (e) {
if (isAccountAlreadyExists(e)) {
redirectToLogin(email)
return
}
throw e
} Prevention
- Check email availability before showing the signup form.
- Disable the submit button while signup is in-flight to avoid double submission.
- Route duplicate-email users to login/password-reset instead of signup.
When it happens
Trigger: Calling signUpOtp with an email whose social id (emailSocialId) is already linked to an account — i.e. the person already completed signup, or a partial signup previously created the account.
Common situations: Double-submitting the signup form; retrying signup after a timeout when the first request actually succeeded; users signing up again after switching login methods; migration leaving a verified social id attached to an existing account.
Related errors
- platform.status.AccountNotFound
- AccountNotFound
- BadRequest
- SocialIdNotFound
- platform.status.SocialIdAlreadyExists
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/be80f80b61b3b95a.
Report an issue: GitHub.