fullstackhero/dotnet-starter-kit · error · NotFoundException
User was not found.
Error message
User {userId} was not found. What it means
Thrown as a NotFoundException by AdminConfirmEmailAsync when no user with the given userId exists in the current tenant's user set. The service queries userManager.Users filtered by Id and tenant, so a wrong ID, a cross-tenant ID, or an already-deleted user produces this error. It is a deliberate 404-style signal that the confirmation target does not exist.
Solutions
- Verify the userId exists via a user list/search endpoint before confirming
- Check that the request is hitting the correct tenant (tenant header/claim resolves the expected tenant)
- Confirm the user was not deleted (check the users table including soft-deleted rows if applicable)
- Regenerate/refresh the admin user list so the client isn't holding a stale ID
Example fix
// before
await userRegistrationService.AdminConfirmEmailAsync(userId, ct);
// after
var exists = await userManager.Users.AnyAsync(u => u.Id == userId, ct);
if (!exists) return Results.NotFound($"User {userId} does not exist in this tenant.");
await userRegistrationService.AdminConfirmEmailAsync(userId, ct); Defensive patterns
Strategy: validation
Validate before calling
bool userExists = await userManager.Users.AnyAsync(u => u.Id == userId, cancellationToken);
Type guard
if (userId == Guid.Empty) throw new ArgumentException("userId must be a non-empty Guid"); Try / catch
catch (NotFoundException ex) { return Results.NotFound(new { ex.Message }); } Prevention
- Always resolve userId from a freshly fetched user list, never long-lived client state
- Include tenant resolution in integration tests so cross-tenant IDs fail loudly
- Log the resolved tenant id alongside userId for diagnosis
When it happens
Trigger: Calling AdminConfirmEmailAsync with a userId that does not exist, was deleted, or belongs to a different tenant (the query runs under the tenant filter of BaseDbContext).
Common situations: Admin UI passes a stale row ID after the user was deleted; client sends a Guid from another tenant database; typo'd or truncated ID from a copy-paste; tests using random Guids without seeding users.
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/0dcd9f902001ac37.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:99
_ = 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;
var result = await userManager.UpdateAsync(user);
if (!result.Succeeded)
{
throw new CustomException(string.Format(
CultureInfo.InvariantCulture,
"An error occurred while confirming the email for {0}: {1}",
user.Email,
string.Join("; ", result.Errors.Select(e => e.Description))));
}
}View on GitHub (pinned to 3f2959e683)