hcengineering/platform · error · PlatformError

PasswordLoginLocked

PasswordLoginLocked

Error message

Login attempt on locked account - password login locked

What it means

During login, the account exists but is password-locked because of too many failed login attempts (isAccountPasswordLocked). The server throws PasswordLoginLocked, carrying the account email, before any password check is performed.

Source

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

  const normalizedEmail = cleanEmail(email)

  try {
    const emailSocialId = await getEmailSocialId(db, normalizedEmail)

    if (emailSocialId == null) {
      throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, {}))
    }

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

    if (existingAccount == null) {
      throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, {}))
    }

    // Check if account is locked due to too many failed login attempts
    if (isAccountPasswordLocked(existingAccount)) {
      ctx.warn('Login attempt on locked account - password login locked', {
        email: normalizedEmail,
        failedAttempts: existingAccount.failedLoginAttempts
      })
      throw new PlatformError(
        new Status(Severity.ERROR, platform.status.PasswordLoginLocked, { account: normalizedEmail })
      )
    }

    const person = await db.person.findOne({ uuid: emailSocialId.personUuid })
    if (person == null) {
      throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {}))
    }

    if (!verifyPassword(password, existingAccount.hash, existingAccount.salt)) {
      try {
        await recordFailedLoginAttempt(db, existingAccount.uuid)
      } catch (err) {
        ctx.warn('Failed to record failed login attempt', { error: err, account: existingAccount.uuid })

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Wait for the lock window to expire, then log in with correct credentials.
  2. Use the password reset / restorePassword flow to reset credentials and clear the lock state.
  3. An admin can clear failedLoginAttempts / the lock flag on the account record.
  4. For service accounts, update the stored secret wherever it is embedded after rotation.
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function isPasswordLoginLocked(e: unknown): boolean {
  return e instanceof PlatformError && e.status.code === platform.status.PasswordLoginLocked
}

Try / catch

try {
  await client.login(email, password)
} catch (e) {
  if (isPasswordLoginLocked(e)) {
    showAccountLockedMessage(email)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: A user calls login (or restorePassword triggers a login flow) with the correct or incorrect password for an account whose failedLoginAttempts exceeded the lock threshold.

Common situations: Users mistyping passwords repeatedly; credential-stuffing bots hammering a login endpoint; a shared service account rotated without updating all consumers; password manager autofill with stale credentials.

Related errors


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