bytebase/bytebase · error

password was reset, but failed to clear the login attempt co

Error message

password was reset, but failed to clear the login attempt counter

What it means

After (optionally) changing the password, ResetUserPassword clears the user's failed-login counter via store.ClearLoginAttempt so an existing lockout does not keep Login returning ResourceExhausted with the new password. If that clear fails AND the password was actually changed (result.Changed=true), the result is returned together with this error: the reset succeeded but the lockout persists.

Source

Thrown at backend/component/recovery/service.go:377

	result := &ResetUserPasswordResult{WorkspaceID: request.WorkspaceID, Email: email}
	if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), request.Password) != nil {
		passwordHash, err := bcrypt.GenerateFromPassword(request.Password, bcrypt.DefaultCost)
		if err != nil {
			return nil, errors.Wrap(err, "failed to hash user password")
		}
		if _, err := s.store.UpdateUser(ctx, user, &store.UpdateUserMessage{PasswordHash: new(string(passwordHash))}); err != nil {
			return nil, errors.Wrap(err, "failed to reset user password")
		}
		result.Changed = true
	}

	// A lockout the user guessed themselves into must not outlive the reset:
	// without this, the operator hands over a password that Login keeps
	// refusing with ResourceExhausted until the window lapses.
	if err := s.store.ClearLoginAttempt(ctx, email, storepb.LoginAttemptKind_PASSWORD); err != nil {
		if result.Changed {
			return result, errors.Wrap(err, "password was reset, but failed to clear the login attempt counter")
		}
		return result, errors.Wrap(err, "failed to clear the login attempt counter")
	}

	if err := s.createAuditLog(ctx, request.WorkspaceID, resetUserPasswordAuditMethod, string(auditRequest)); err != nil {
		return result, errors.Wrap(err, "user password reset completed, but failed to create the recovery audit log")
	}
	return result, nil
}

func (s *Service) getActiveEndUser(ctx context.Context, email string) (*store.UserMessage, error) {
	account, err := s.store.GetAccountByEmail(ctx, email)
	if err != nil {
		return nil, errors.Wrap(err, "failed to find user identity")
	}
	if account == nil {
		return nil, errors.Errorf("user %q does not exist", email)
	}

View on GitHub (pinned to 1870550677)

Solutions

  1. Inspect the wrapped inner error and retry ResetUserPassword — it is safe because the bcrypt compare makes the second run a no-op for the password, and it will re-attempt ClearLoginAttempt.
  2. Manually clear the login-attempt record for the email (PASSWORD kind) if the API keeps retry-failing.
  3. Verify the login-attempt table exists and is writable (migrations, privileges).
  4. Inform the operator that the new password may still be refused with ResourceExhausted until the lockout window lapses if the counter cannot be cleared.

Example fix

// before
res, err := svc.ResetUserPassword(ctx, req)
if err != nil { return err } // password changed but lockout still active
// after
res, err := svc.ResetUserPassword(ctx, req)
if err != nil {
    if res != nil && res.Changed {
        // password WAS reset; retry just the counter clear / inform operator
        log.Printf("password reset ok, clearing lockout failed: %v", err)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check DB writability of both user and login-attempt storage first
if err := store.Ping(ctx); err != nil {
    return fmt.Errorf("metadata DB unstable, partial reset likely: %w", err)
}

Try / catch

res, err := svc.ResetUserPassword(ctx, req)
if err != nil {
    if res != nil && res.Changed {
        // password was reset; lockout may persist — retry to clear the counter
        res, err = svc.ResetUserPassword(ctx, req)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling ResetUserPassword for a user whose password was updated when ClearLoginAttempt fails: DB write failure on the login-attempt table, connection drop between UpdateUser and ClearLoginAttempt, or a missing login-attempt table/index from incomplete migrations.

Common situations: User was locked out from repeated wrong passwords and the operator resets them during a DB instability; partial migration left the login-attempt storage inconsistent; transient network failure between the two store calls.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/018a99af37d39781. Report an issue: GitHub.