toeverything/AFFiNE · warning · SameEmailProvided

same_email_provided

same_email_provided

Error message

You are trying to update your account email to the same as the old one.

What it means

Thrown by sendVerifyChangeEmail when getUserByEmail(email) resolves to the current user's own account (hasRegistered.id === user.id). The flow refuses to run a no-op email change to the address the user already owns.

Solutions

  1. Compare the entered email against currentUser.email client-side and block submission when equal
  2. Show the current email next to the input so the change target is obvious
  3. If hit in tests, update fixtures to use a genuinely different address

Example fix

// before
await client.request(sendVerifyChangeEmailMutation, { token, email, callbackUrl });

// after
if (email === me.email) {
  setNotice('This is already your email address');
  return;
}
await client.request(sendVerifyChangeEmailMutation, { token, email, callbackUrl });
Defensive patterns

Strategy: validation

Validate before calling

const normalized = email.trim().toLowerCase();
if (normalized === me.email) {
  setNotice('This is already your email address');
} else {
  await client.request(sendVerifyChangeEmailMutation, { token, email: normalized, callbackUrl });
}

Prevention

When it happens

Trigger: Calling sendVerifyChangeEmail with an email identical to the signed-in user's current email.

Common situations: User retypes their existing address out of habit; form pre-filled with the current email and submitted unchanged; copy/paste of the wrong row from a password manager.

Related errors


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

Appendix: source

Thrown at packages/backend/server/src/core/auth/resolver.ts:278

    const valid = await this.models.verificationToken.verify(
      TokenType.ChangeEmail,
      token,
      {
        credential: user.id,
      }
    );

    if (!valid) {
      throw new InvalidEmailToken();
    }

    const hasRegistered = await this.models.user.getUserByEmail(email);

    if (hasRegistered) {
      if (hasRegistered.id !== user.id) {
        throw new EmailAlreadyUsed();
      } else {
        throw new SameEmailProvided();
      }
    }

    const { token: verifyEmailToken, expiresAt } =
      await this.models.verificationToken.createWithExpiresAt(
        TokenType.VerifyEmail,
        user.id
      );

    const url = this.url.safeLink(callbackUrl, {
      token: verifyEmailToken,
      email,
    });
    return await this.auth.sendVerifyChangeEmail(
      email,
      url,
      this.mailMetadata(context, expiresAt)
    );

View on GitHub (pinned to b4c8548c09)