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
- Check the user's emailConfirmed status in the UI before offering the resend action
- Treat this 400 as success in the client (email is already confirmed) and show an informative message
- Disable the resend button after the first click and poll confirmation status
- 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
- Hide the resend action once EmailConfirmed is true in the UI
- Disable resend buttons after click (debounce)
- Poll confirmation status after sending the email instead of blind retries
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
- An error occurred while confirming E-Mail.
- An error occurred while confirming
- Group with ID ' ' not found.
- Users not found
- Group with name ' ' already exists.
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)