toeverything/AFFiNE · error · WrongSignInCredentials

wrong_sign_in_credentials

wrong_sign_in_credentials

Error message

Wrong user email or password: ${email}

What it means

While sending a magic link, MagicLinkService.load looks the user up with withDisabled: true; if the account exists but is flagged disabled, it throws WrongSignInCredentials (wrong_sign_in_credentials) - the same error as a wrong password, deliberately, so callers cannot distinguish 'disabled account' from 'bad credentials' for enumeration reasons. The disabled user never receives the email.

Solutions

  1. An administrator must re-enable the account in user management before sign-in can proceed
  2. If you own the instance, check the user's disabled flag in the admin UI / database
  3. Sign in with a different, active account
  4. Do not brute-force variants of the email - the error intentionally does not confirm the account's state
Defensive patterns

Strategy: try-catch

Type guard

function isWrongSignInCredentials(e: unknown): boolean {
  return typeof e === 'object' && e !== null && (e as { code?: string }).code === 'wrong_sign_in_credentials';
}

Try / catch

try {
  await sendMagicLink(email);
} catch (e) {
  if (isWrongSignInCredentials(e)) {
    show('Email or password is incorrect, or this account is not active.'); // do not probe further
  } else throw e;
}

Prevention

When it happens

Trigger: A deactivated/banned user (or one disabled by an admin) requests a magic-link sign-in; user was disabled during workspace cleanup but still has the app open; admin disabled the account and the user tries the passwordless flow instead of password.

Common situations: Offboarded employees retrying sign-in; self-hosted admins disabling test accounts; SSO provisioning disabling local accounts; users confused because the same error also appears for a genuinely wrong email/password.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/7d190036b2e9d2c1. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/auth/magic-link.ts:62

    const callbackUrlObj = this.url.url(callbackUrl);
    const redirectUriInCallback =
      callbackUrlObj.searchParams.get('redirect_uri');
    if (
      redirectUriInCallback &&
      !this.url.isAllowedRedirectUri(redirectUriInCallback)
    ) {
      throw new ActionForbidden();
    }

    const user = await this.models.user.getUserByEmail(email, {
      withDisabled: true,
    });

    if (!user) {
      await this.assertSignupAllowed(email);
    } else if (user.disabled) {
      throw new WrongSignInCredentials({ email });
    }

    const ttlInSec = 30 * 60;
    const { token, expiresAt: tokenExpiresAt } =
      await this.models.verificationToken.createWithExpiresAt(
        TokenType.SignIn,
        email,
        ttlInSec
      );

    const otp = this.crypto.otp();
    const { expiresAt: otpExpiresAt } = await this.models.magicLinkOtp.upsert(
      email,
      otp,
      token,
      clientNonce
    );

View on GitHub (pinned to b4c8548c09)