fullstackhero/dotnet-starter-kit · error · CustomException
An error occurred while confirming
Error message
An error occurred while confirming {0} What it means
ConfirmEmailAsync throws CustomException('An error occurred while confirming {0}') when UserManager.ConfirmEmailAsync returns a failed IdentityResult — the user was found and unconfirmed, but the provided code did not validate (wrong, expired, or corrupted token). The message includes the email to help the user retry.
Solutions
- Request a new confirmation email and use the freshest link/code.
- Ensure the full code is copied — check the email client didn't wrap or truncate the URL token.
- Avoid changing the password or security stamp between requesting and confirming; re-request if you did.
- Verify data-protection key persistence across instances/restarts so issued tokens remain valid.
Example fix
// caller: send the pristine code from the email link unchanged
// service: log identity errors for diagnosis
if (!result.Succeeded)
{
var errors = string.Join("; ", result.Errors.Select(e => e.Description));
logger.LogWarning("Email confirmation failed for {Email}: {Errors}", user.Email, errors);
throw new CustomException(string.Format(CultureInfo.InvariantCulture, "An error occurred while confirming {0}", user.Email));
} Defensive patterns
Strategy: retry
Validate before calling
// ensure the full code arrived intact
const code = new URLSearchParams(location.search).get('code');
if (!code || code.length < 20 || /[\s]/.test(code)) showError('Confirmation link appears incomplete — request a new email.'); Try / catch
try { await confirmEmail(userId, code); }
catch (e) { if (isBadRequest(e)) { await resendConfirmation(email); show('We sent a fresh confirmation link.'); } else { throw e; } } Prevention
- Request a new confirmation email if password or profile changed since the link was issued (security stamp invalidates tokens).
- Use HTML email templates that prevent URL line-wrapping/truncation.
- Persist data-protection keys across instances so tokens stay valid.
- Never hand-edit or trim the code parameter from links.
When it happens
Trigger: The confirmation code was Base64Url-decoded but rejected: token generated for a different purpose/user, invalidated by a security stamp change (password reset, profile update that rotates the stamp), tampering, or truncation of the code from the email client.
Common situations: Email clients wrapping/breaking the long code in the URL; user requested a password reset after the email was generated (stamp changed, old token invalid); link copy-paste losing characters; data-protection keys changed between environments invalidating old tokens.
Related errors
- An error occurred while confirming E-Mail.
- The email for is already confirmed.
- 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/082725d15fb153c6.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:89
return user.Id;
}
public async Task<string> ConfirmEmailAsync(string userId, string code, string tenant, CancellationToken cancellationToken)
{
EnsureValidTenant();
var user = await userManager.Users
.Where(u => u.Id == userId && !u.EmailConfirmed)
.FirstOrDefaultAsync(cancellationToken);
_ = user ?? throw new CustomException("An error occurred while confirming E-Mail.");
code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
var result = await userManager.ConfirmEmailAsync(user, code);
return result.Succeeded
? string.Format(CultureInfo.InvariantCulture, "Account Confirmed for E-Mail {0}. You can now use the /api/tokens endpoint to generate JWT.", user.Email)
: throw new CustomException(string.Format(CultureInfo.InvariantCulture, "An error occurred while confirming {0}", user.Email));
}
public async Task AdminConfirmEmailAsync(string userId, CancellationToken cancellationToken = default)
{
EnsureValidTenant();
var user = await userManager.Users
.Where(u => u.Id == userId)
.FirstOrDefaultAsync(cancellationToken)
?? throw new NotFoundException($"User {userId} was not found.");
// Idempotent: a second confirm is a no-op rather than an error.
if (user.EmailConfirmed)
{
return;
}
user.EmailConfirmed = true;View on GitHub (pinned to 3f2959e683)