fullstackhero/dotnet-starter-kit · error · NotFoundException
user not found
Error message
user not found
What it means
ResetPasswordAsync throws NotFoundException when userManager.FindByEmailAsync(email) returns null, i.e. no user with that email exists in the current tenant. The reset cannot proceed without the user record that owns the password.
Solutions
- Verify the email address is spelled correctly and matches the registered user
- Ensure the request resolves to the same tenant the user belongs to (send the tenant identifier)
- Check the user row exists in the AspNetUsers table for that tenant/email
- If the user was deleted, re-register the account before resetting the password
Example fix
var user = await userManager.FindByEmailAsync(email);
if (user is null) return Results.NotFound($"no user with email {email}");
await passwordService.ResetPasswordAsync(email, token, newPassword, ct); Defensive patterns
Strategy: validation
Validate before calling
var normalized = email?.Trim().ToLowerInvariant();
if (string.IsNullOrWhiteSpace(normalized) || !new EmailAddressAttribute().IsValid(normalized))
return Results.BadRequest("A valid email is required.");
var exists = await userManager.Users.AnyAsync(u => u.NormalizedEmail == userManager.NormalizeEmail(normalized));
if (!exists) return Results.NotFound("No account found for this email in the current tenant."); Try / catch
try
{
await passwordService.ResetPasswordAsync(email, token, newPassword, ct);
}
catch (NotFoundException)
{
return Results.NotFound("No account found for this email in the current tenant.");
} Prevention
- Confirm the user exists in the target tenant before sending reset links
- Normalize/trim email input client-side
- Remember users are tenant-scoped — same email may exist only in another tenant
When it happens
Trigger: Calling ResetPasswordAsync(email, token, password, ...) with an email that has no matching FshUser — wrong email typed, user deleted, or the user exists in a different tenant.
Common situations: Client mistyped the email; calling reset in the wrong tenant context (users are tenant-scoped); user was removed between requesting the reset token and submitting it; test data never seeded the user.
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
- Group with ID ' ' not found.
- Roles not found
- role not found
- Origin URL is not configured.
- User with ID ' ' not found.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/722b2982533e619d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs:69
["email"] = email,
["tenant"] = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id,
});
var mailRequest = new MailRequest(
new Collection<string> { user.Email },
"Reset Password",
$"Please reset your password using the following link: {resetPasswordUri}");
jobService.Enqueue(() => mailService.SendAsync(mailRequest, CancellationToken.None));
}
public async Task ResetPasswordAsync(string email, string password, string token, CancellationToken cancellationToken)
{
EnsureValidTenant();
var user = await userManager.FindByEmailAsync(email);
if (user == null)
{
throw new NotFoundException("user not found");
}
token = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(token));
var result = await userManager.ResetPasswordAsync(user, token, password);
if (!result.Succeeded)
{
var errors = result.Errors.Select(e => e.Description).ToList();
throw new CustomException("error resetting password", errors);
}
// Raise domain event for password reset
var tenantId = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;
user.RecordPasswordChanged(wasReset: true, tenantId);
await db.SaveChangesAsync(cancellationToken);
}
public async Task ChangePasswordAsync(string password, string newPassword, string confirmNewPassword, string userId, CancellationToken cancellationToken = default)View on GitHub (pinned to 3f2959e683)