fullstackhero/dotnet-starter-kit · error · CustomException
An error occurred while confirming phone number
Error message
An error occurred while confirming phone number {0} What it means
Thrown as a CustomException when userManager.ChangePhoneNumberAsync returns a failed IdentityResult — most commonly because the supplied code is invalid, expired, or malformed after Base64Url decoding. The message includes the phone number for context. It means the code could not be verified against the generated phone-confirmation token.
Solutions
- Have the user re-enter the code carefully, then request a fresh code if it fails again
- Verify the code is passed unmodified (no extra URL encoding/trimming) to the API
- If the security stamp changed (password reset, 2FA change), regenerate the phone confirmation token
- Check clock skew and token lifetime settings if codes expire prematurely
Example fix
// before
var msg = await service.ConfirmPhoneNumberAsync(userId, rawCode, ct);
// after
try
{
var msg = await service.ConfirmPhoneNumberAsync(userId, Uri.UnescapeDataString(rawCode.Trim()), ct);
}
catch (CustomException)
{
await service.SendPhoneCodeAsync(userId); // fresh code
throw new InvalidCodeError("Code invalid or expired — a new code has been sent.");
} Defensive patterns
Strategy: validation
Validate before calling
string normalized = code?.Trim(); if (string.IsNullOrWhiteSpace(normalized) || normalized.Any(char.IsWhiteSpace)) throw new ArgumentException("Code must be a non-empty Base64Url string"); Type guard
bool IsValidBase64Url(string s) => !string.IsNullOrWhiteSpace(s) && s.All(c => char.IsAsciiLetterOrDigit(c) || c is '-' or '_');
Try / catch
catch (CustomException ex) { await resendNewCodeAsync(userId); return Results.BadRequest("Code invalid or expired — a new code was sent."); } Prevention
- Pass the code through unmodified (beware double URL-encoding in query strings)
- Set realistic token lifetimes and inform users of expiry
- Always offer a 'resend code' path; treat failure as regenerate-and-retry
When it happens
Trigger: Submitting an incorrect SMS code; a code that expired (security-stamp or token lifetime changed); reusing an already-consumed code; code string mangled in transit (padding/url-encoding) so it decodes to the wrong bytes.
Common situations: User types the code wrong; SMS delayed and token expires; two confirmation attempts where the first consumed the token; proxies altering the Base64Url token in query strings.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- An error occurred while confirming phone number.
- Group with ID ' ' not found.
- Users not found
- Group with name ' ' already exists.
- Roles not found
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/adf9808371e1613c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:154
await SendConfirmationEmailAsync(user, origin, cancellationToken);
}
public async Task<string> ConfirmPhoneNumberAsync(string userId, string code, CancellationToken cancellationToken = default)
{
EnsureValidTenant();
var user = await userManager.Users
.Where(u => u.Id == userId && !u.PhoneNumberConfirmed)
.FirstOrDefaultAsync(cancellationToken);
_ = user ?? throw new CustomException("An error occurred while confirming phone number.");
code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
var result = await userManager.ChangePhoneNumberAsync(user, user.PhoneNumber!, code);
return result.Succeeded
? string.Format(CultureInfo.InvariantCulture, "Phone number {0} confirmed successfully.", user.PhoneNumber)
: throw new CustomException(string.Format(CultureInfo.InvariantCulture, "An error occurred while confirming phone number {0}", user.PhoneNumber));
}
private void EnsureValidTenant()
{
if (string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id))
{
throw new UnauthorizedException("invalid tenant");
}
}
private static string ExtractEmailFromPrincipal(ClaimsPrincipal principal)
{
return principal.FindFirstValue(ClaimTypes.Email)
?? principal.FindFirstValue("email")
?? throw new CustomException("Email claim is required for external authentication.");
}
private async Task<FshUser> CreateUserFromPrincipalAsync(ClaimsPrincipal principal, string email)View on GitHub (pinned to 3f2959e683)