fullstackhero/dotnet-starter-kit · error · CustomException
error resetting password
Error message
error resetting password
What it means
ResetPasswordAsync throws CustomException('error resetting password') when userManager.ResetPasswordAsync fails (result.Succeeded == false). The Identity result errors (e.g. invalid/expired token, weak password) are attached to the exception as a list of descriptions.
Solutions
- Inspect the errors collection attached to the exception — it names the exact Identity failure
- Request a fresh password-reset token and retry promptly before it expires
- Ensure the token is sent exactly as generated (Base64Url encoded, no trimming/HTML unescaping)
- Choose a password satisfying the configured Identity password options
Example fix
// before: swallowing the detail
catch (CustomException) { return Results.BadRequest("reset failed"); }
// after
catch (CustomException ex) { return Results.BadRequest(new { ex.Message, errors = ex.Errors }); } Defensive patterns
Strategy: try-catch
Validate before calling
var policyErrors = newPassword is null || newPassword.Length < 8
? new List<string> { "Password must be at least 8 characters." }
: new List<string>();
if (policyErrors.Count > 0) return Results.BadRequest(policyErrors); Try / catch
try
{
await passwordService.ResetPasswordAsync(email, token, newPassword, ct);
}
catch (CustomException ex)
{
return Results.BadRequest(new { message = ex.Message, identityErrors = ex.Errors });
} Prevention
- Use reset tokens promptly — they expire quickly
- Never re-encode or trim the Base64Url token between generation and submission
- Enforce password policy client-side to mirror Identity options
- Avoid issuing multiple reset tokens for one attempt; always use the latest
When it happens
Trigger: Submitting a reset token that is invalid, already used, or expired; new password violating the configured Identity password policy (length, complexity, reuse); token mangled by incorrect Base64Url encoding/decoding.
Common situations: Reset link older than the token lifetime; user requested multiple resets and used the older token; client double-encoded/decoded the Base64Url token; new password rejected by policy (e.g. too similar to old or missing special char).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- System role permissions are managed by the framework and…
- The authenticator code is invalid.
- Origin URL is not configured.
- UserId must be provided.
- user not found
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/86e3cac1d8f24779.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs:78
}
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)
{
var user = await userManager.FindByIdAsync(userId);
_ = user ?? throw new NotFoundException("user not found");
var result = await userManager.ChangePasswordAsync(user, password, newPassword);
if (!result.Succeeded)
{View on GitHub (pinned to 3f2959e683)