{"record":{"id":"254c42a1f04851ae","repo":"fullstackhero/dotnet-starter-kit","slug":"failed-to-change-password","errorCode":null,"errorMessage":"failed to change password","messagePattern":"failed to change password","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs","lineNumber":98,"sourceCode":"\n        // Raise domain event for password reset\n        var tenantId = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;\n        user.RecordPasswordChanged(wasReset: true, tenantId);\n        await db.SaveChangesAsync(cancellationToken);\n    }\n\n    public async Task ChangePasswordAsync(string password, string newPassword, string confirmNewPassword, string userId, CancellationToken cancellationToken = default)\n    {\n        var user = await userManager.FindByIdAsync(userId);\n\n        _ = user ?? throw new NotFoundException(\"user not found\");\n\n        var result = await userManager.ChangePasswordAsync(user, password, newPassword);\n\n        if (!result.Succeeded)\n        {\n            var errors = result.Errors.Select(e => e.Description).ToList();\n            throw new CustomException(\"failed to change password\", errors);\n        }\n\n        // Raise domain event for password change\n        var tenantId = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;\n        user.RecordPasswordChanged(wasReset: false, tenantId);\n        await db.SaveChangesAsync(cancellationToken);\n\n        // Update password expiry date\n        await passwordExpiryService.UpdateLastPasswordChangeDateAsync(userId, cancellationToken);\n\n        // Save to history\n        await passwordHistoryService.SavePasswordHistoryAsync(userId, cancellationToken);\n    }\n\n    private void EnsureValidTenant()\n    {\n        if (string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id))\n        {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs#L80-L116","documentation":"ChangePasswordAsync throws CustomException('failed to change password') when userManager.ChangePasswordAsync returns result.Succeeded == false. Typical Identity failures: wrong current password, new password below policy, or password reuse rules; the Identity error descriptions are attached to the exception.","triggerScenarios":"Calling ChangePasswordAsync with an incorrect current password; newPassword violating strength/length/uniqueness policy; confirmNewPassword mismatching newPassword is caught earlier by validation, but Identity-side policy failures surface here.","commonSituations":"User typo in the current password; new password too weak per configured Identity options; account locked or password recently changed violating reuse policy; client sends untrimmed input with stray whitespace.","solutions":["Read the errors list on the exception — Identity reports the exact reason (e.g. 'PasswordTooShort', 'PasswordMismatch')","Retry with the correct current password","Pick a password meeting the configured Identity strength requirements","Normalize/trim inputs client-side before submission"],"exampleFix":"// before\nthrow new Exception(\"change failed\");\n// after\nvar errors = result.Errors.Select(e => e.Description).ToList();\nthrow new CustomException(\"failed to change password\", errors);","handlingStrategy":"try-catch","validationCode":"if (newPassword != confirmNewPassword)\n    return Results.BadRequest(\"Passwords do not match.\");\nif (newPassword is null || newPassword.Length < 8)\n    return Results.BadRequest(\"Password must be at least 8 characters.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await passwordService.ChangePasswordAsync(currentPassword, newPassword, confirmNewPassword, userId, ct);\n}\ncatch (CustomException ex)\n{\n    return Results.BadRequest(new { message = ex.Message, identityErrors = ex.Errors });\n}","preventionTips":["Surface the Identity error descriptions (e.g. PasswordMismatch, PasswordTooShort) in the UI","Mirror the configured Identity password policy client-side","Trim/normalize inputs before submission","Prompt the user to re-enter the current password after a PasswordMismatch failure"],"tags":["identity","password-change","validation"],"backgroundTag":"schema-validation-failed","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"}