hcengineering/platform · error · PlatformError

AccountNotFound

AccountNotFound

Error message

platform.status.AccountNotFound

What it means

Thrown by the account service when looking up an account by a normalized email during email-based social ID operations (e.g. email join/invite flows). The email social ID resolved to a personUuid, but no account exists for that UUID in the database, indicating dangling or inconsistent social-ID data. The Status carries the missing account UUID.

Source

Thrown at server/account/src/operations.ts:1519

  const normalizedEmail = cleanEmail(email)

  ctx.info('Requesting password reset', { email, normalizedEmail })

  const emailSocialId = await getEmailSocialId(db, normalizedEmail)

  if (emailSocialId == null) {
    ctx.error('Email social id not found', { email, normalizedEmail })
    throw new PlatformError(
      new Status(Severity.ERROR, platform.status.SocialIdNotFound, { value: email, type: SocialIdType.EMAIL })
    )
  }

  const account = await getAccount(db, emailSocialId.personUuid as AccountUuid)

  if (account == null) {
    ctx.info('Account not found', { email, normalizedEmail })
    throw new PlatformError(
      new Status(Severity.ERROR, platform.status.AccountNotFound, { account: emailSocialId.personUuid })
    )
  }

  const { mailURL, mailAuth } = getMailUrl()
  const front = getFrontUrl(branding)

  const token = generateToken(account.uuid, undefined, {
    restoreEmail: normalizedEmail
  })

  const link = concatLink(front, `/login/recovery?id=${token}`)
  const lang = branding?.language
  const text = await translate(accountPlugin.string.RecoveryText, { link }, lang)
  const html = await translate(accountPlugin.string.RecoveryHTML, { link }, lang)
  const subject = await translate(accountPlugin.string.RecoverySubject, {}, lang)

  const response = await fetch(concatLink(mailURL, '/send'), {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Check the account exists for the personUuid (getAccount) before invoking the email-based operation.
  2. Clean up orphaned socialId documents whose personUuid has no matching account.
  3. If migrating/restoring workspaces, ensure accounts were migrated to the same instance first.
  4. Re-trigger the signup flow so the account is created before joining.

Example fix

// before: assume social id implies account
const account = await getAccount(db, emailSocialId.personUuid as AccountUuid)
await join(ctx, emailSocialId.personUuid)
// after: guard first
const account = await getAccount(db, emailSocialId.personUuid as AccountUuid)
if (account == null) {
  await signupAndCreateAccount(ctx, email)
}
Defensive patterns

Strategy: validation

Validate before calling

const account = await getAccount(db, emailSocialId.personUuid as AccountUuid)
if (account == null) {
  // recreate account or clean up orphaned socialId before proceeding
}

Type guard

function accountExists(a: Account | null | undefined): a is Account { return a != null }

Try / catch

try {
  await joinByEmail(ctx, email)
} catch (err) {
  if (err instanceof PlatformError && err.status.code === platform.status.AccountNotFound) {
    await cleanupOrphanedSocialId(email); await reSignup(email)
  } else throw err
}

Prevention

When it happens

Trigger: Calling an account operation (e.g. joinByEmailSocialId) where db social ID lookup for SocialIdType.EMAIL yields a personUuid whose account row was deleted or never created.

Common situations: Account deletion left orphaned social ID records; workspace backups/restores referencing accounts from another instance; email reused across regions; race between account creation and email verification.

Related errors


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