fullstackhero/dotnet-starter-kit · error · CustomException

An error occurred while confirming E-Mail.

Error message

An error occurred while confirming E-Mail.

What it means

ConfirmEmailAsync throws CustomException('An error occurred while confirming E-Mail.') when no unconfirmed user matching the given userId exists (the query filters to Id == userId && !EmailConfirmed). It is deliberately vague to avoid leaking whether an account exists or is already confirmed.

Solutions

  1. If the account already works, just sign in — the error often means confirmation already completed.
  2. Request a fresh confirmation email to get a new valid userId+code pair.
  3. Verify the confirmation link points at the correct tenant host so the user row is visible.
  4. Re-send registration/confirmation if the DB was re-seeded and the old link's user no longer exists.
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: only fire confirmation once, and detect already-confirmed
const { data: me } = await tryGetCurrentUser();
if (me?.emailConfirmed) redirect('/login');

Try / catch

try { await confirmEmail(userId, code); }
catch (e) { if (e.status === 400 && /confirming E-Mail/.test(e.detail)) { promptLogin('Account may already be confirmed.'); } else { throw e; } }

Prevention

When it happens

Trigger: Clicking an email-confirmation link after the account was already confirmed; a tampered or mismatched userId in the link; the user row being deleted; tenant resolution directing the query to a tenant without that user.

Common situations: Users double-clicking confirmation links or reusing an old email; expired/stale links from before a database re-seed; wrong tenant host in the confirmation URL so the user isn't found in that tenant's store.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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

Appendix: source

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

        ValidatePasswordMatch(password, confirmPassword);

        var user = await CreateUserWithPasswordAsync(firstName, lastName, email, userName, password, phoneNumber);
        await AssignDefaultRoleAndGroupsAsync(user, "System", cancellationToken);
        await SendConfirmationEmailAsync(user, origin, cancellationToken);
        await PublishUserRegisteredAsync(user, "Identity", cancellationToken);

        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.");

View on GitHub (pinned to 3f2959e683)