fullstackhero/dotnet-starter-kit · warning · CustomException

The email for is already confirmed.

Error message

The email for {0} is already confirmed.

What it means

Thrown as a CustomException when ResendConfirmationEmailAsync is called for a user whose EmailConfirmed is already true. Resending a confirmation email to an already-confirmed account is treated as a client-side error (400-style) rather than a silent no-op — unlike AdminConfirmEmailAsync which is idempotent. The intent is to stop unnecessary emails.

Solutions

  1. Check the user's emailConfirmed status in the UI before offering the resend action
  2. Treat this 400 as success in the client (email is already confirmed) and show an informative message
  3. Disable the resend button after the first click and poll confirmation status
  4. Debounce/deduplicate resend calls in the frontend

Example fix

// before
await service.ResendConfirmationEmailAsync(userId, origin, ct);
// after
try
{
    await service.ResendConfirmationEmailAsync(userId, origin, ct);
}
catch (CustomException) when (emailAlreadyConfirmed)
{
    toast.Info("This email is already confirmed. You can sign in.");
}
Defensive patterns

Strategy: try-catch

Validate before calling

var confirmed = await userManager.Users.Where(u => u.Id == userId).Select(u => u.EmailConfirmed).FirstOrDefaultAsync(); if (confirmed) return;

Type guard

if (user?.EmailConfirmed == true) return Results.Ok("already confirmed");

Try / catch

catch (CustomException) when (message.Contains("already confirmed")) { return Results.Ok("Email is already confirmed."); }

Prevention

When it happens

Trigger: Double-click on 'resend confirmation' after the user already clicked the emailed link; calling resend from an admin flow without checking confirmation status; a resend request racing with the user's own email confirmation.

Common situations: User confirms via email then clicks resend in an old browser tab; automated retry job resending to already-confirmed users; frontend not refreshing user state after confirmation.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/f7a0fe938f9de746. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:130

                CultureInfo.InvariantCulture,
                "An error occurred while confirming the email for {0}: {1}",
                user.Email,
                string.Join("; ", result.Errors.Select(e => e.Description))));
        }
    }

    public async Task ResendConfirmationEmailAsync(string userId, string origin, CancellationToken cancellationToken = default)
    {
        EnsureValidTenant();

        var user = await userManager.Users
            .Where(u => u.Id == userId)
            .FirstOrDefaultAsync(cancellationToken)
            ?? throw new NotFoundException($"User {userId} was not found.");

        if (user.EmailConfirmed)
        {
            throw new CustomException(string.Format(
                CultureInfo.InvariantCulture,
                "The email for {0} is already confirmed.",
                user.Email));
        }

        await SendConfirmationEmailAsync(user, origin, cancellationToken);
    }

    public async Task<string> ConfirmPhoneNumberAsync(string userId, string code, CancellationToken cancellationToken = default)
    {
        EnsureValidTenant();

        var user = await userManager.Users
            .Where(u => u.Id == userId && !u.PhoneNumberConfirmed)
            .FirstOrDefaultAsync(cancellationToken);

        _ = user ?? throw new CustomException("An error occurred while confirming phone number.");

View on GitHub (pinned to 3f2959e683)