{"record":{"id":"26ef4be3b168172c","repo":"fullstackhero/dotnet-starter-kit","slug":"an-error-occurred-while-confirming-e-mail","errorCode":null,"errorMessage":"An error occurred while confirming E-Mail.","messagePattern":"An error occurred while confirming E-Mail\\.","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs","lineNumber":82,"sourceCode":"        ValidatePasswordMatch(password, confirmPassword);\n\n        var user = await CreateUserWithPasswordAsync(firstName, lastName, email, userName, password, phoneNumber);\n        await AssignDefaultRoleAndGroupsAsync(user, \"System\", cancellationToken);\n        await SendConfirmationEmailAsync(user, origin, cancellationToken);\n        await PublishUserRegisteredAsync(user, \"Identity\", cancellationToken);\n\n        return user.Id;\n    }\n\n    public async Task<string> ConfirmEmailAsync(string userId, string code, string tenant, CancellationToken cancellationToken)\n    {\n        EnsureValidTenant();\n\n        var user = await userManager.Users\n            .Where(u => u.Id == userId && !u.EmailConfirmed)\n            .FirstOrDefaultAsync(cancellationToken);\n\n        _ = user ?? throw new CustomException(\"An error occurred while confirming E-Mail.\");\n\n        code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));\n        var result = await userManager.ConfirmEmailAsync(user, code);\n\n        return result.Succeeded\n            ? string.Format(CultureInfo.InvariantCulture, \"Account Confirmed for E-Mail {0}. You can now use the /api/tokens endpoint to generate JWT.\", user.Email)\n            : throw new CustomException(string.Format(CultureInfo.InvariantCulture, \"An error occurred while confirming {0}\", user.Email));\n    }\n\n    public async Task AdminConfirmEmailAsync(string userId, CancellationToken cancellationToken = default)\n    {\n        EnsureValidTenant();\n\n        var user = await userManager.Users\n            .Where(u => u.Id == userId)\n            .FirstOrDefaultAsync(cancellationToken)\n            ?? throw new NotFoundException($\"User {userId} was not found.\");\n","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If the account already works, just sign in — the error often means confirmation already completed.","Request a fresh confirmation email to get a new valid userId+code pair.","Verify the confirmation link points at the correct tenant host so the user row is visible.","Re-send registration/confirmation if the DB was re-seeded and the old link's user no longer exists."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// client-side: only fire confirmation once, and detect already-confirmed\nconst { data: me } = await tryGetCurrentUser();\nif (me?.emailConfirmed) redirect('/login');","typeGuard":null,"tryCatchPattern":"try { await confirmEmail(userId, code); }\ncatch (e) { if (e.status === 400 && /confirming E-Mail/.test(e.detail)) { promptLogin('Account may already be confirmed.'); } else { throw e; } }","preventionTips":["Make confirmation links single-use in the UI (disable after click).","Generate confirmation URLs against the correct tenant host.","Re-send confirmation emails rather than reusing old links.","Handle the vague 400 by offering a sign-in attempt first — already-confirmed is the most common cause."],"tags":["identity","email-confirmation","not-found"],"backgroundTag":"user-not-found","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}