fullstackhero/dotnet-starter-kit · error · CustomException
Failed to create user from external principal.
Error message
Failed to create user from external principal.
What it means
Thrown as a CustomException (400) when userManager.CreateAsync fails while provisioning a local user from an external-auth principal. The message is generic on purpose; the specific Identity error descriptions (duplicate email/username, password policy, validator failures) are attached in the errors list. It means the external identity is valid but local account creation was rejected by ASP.NET Identity.
Solutions
- Inspect the errors array on the exception for the exact Identity reason
- If duplicate email, route the user to the external-login linking flow (sign in with password, then link provider) instead of auto-creating
- Adjust generated userName/email normalization to avoid collisions (e.g. append provider id)
- Review custom IUserValidator/IEmailValidator registrations for overly strict rules
Example fix
// before
var user = await userRegistrationService.GetOrCreateFromPrincipalAsync(principal, provider);
// after
try
{
var user = await userRegistrationService.GetOrCreateFromPrincipalAsync(principal, provider);
}
catch (CustomException ex) when (ex.Errors?.Any(e => e.Contains("already taken")) == true)
{
return Results.Conflict("An account with this email exists. Sign in with your password to link the provider.");
} Defensive patterns
Strategy: try-catch
Validate before calling
var email = principal.FindFirstValue(ClaimTypes.Email); var exists = await userManager.Users.AnyAsync(u => u.NormalizedEmail == email!.ToUpperInvariant());
Type guard
bool CanCreate(string email) => !string.IsNullOrWhiteSpace(email) && email.Contains('@'); Try / catch
catch (CustomException ex) when (ex.Errors?.Any(e => e.Contains("already")) == true) { return Results.Conflict("Account exists — sign in and link the provider instead."); } Prevention
- Expose ex.Errors to clients instead of the generic message
- Implement provider linking for existing local accounts
- Generate unique usernames (email prefix + provider id) to avoid collisions
- Test each external provider against a pre-existing same-email local account
When it happens
Trigger: Signing in with an external provider whose email already belongs to an existing local account (duplicate username/email); custom user validators failing; normalized username collisions.
Common situations: User first registered with password, later tries Google sign-in with the same email; two providers issuing the same email; username generated from email exceeding length limits; custom IUserValidator misconfigured.
Related errors
- Failed to generate authenticator key.
- An error occurred while confirming the email for
- Group with ID ' ' not found.
- Users not found
- Group with name ' ' already exists.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/169603701c060bac.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:193
userName = await EnsureUniqueUserNameAsync(userName);
var user = new FshUser
{
Email = email,
UserName = userName,
FirstName = firstName,
LastName = lastName,
EmailConfirmed = true,
PhoneNumberConfirmed = false,
IsActive = true
};
var result = await userManager.CreateAsync(user);
if (!result.Succeeded)
{
var errors = result.Errors.Select(e => e.Description).ToList();
throw new CustomException(
"Failed to create user from external principal.",
errors,
HttpStatusCode.BadRequest);
}
return user;
}
private static (string firstName, string lastName, string userName) ExtractUserInfoFromPrincipal(
ClaimsPrincipal principal, string email)
{
var firstName = principal.FindFirstValue(ClaimTypes.GivenName)
?? principal.FindFirstValue("given_name")
?? string.Empty;
var lastName = principal.FindFirstValue(ClaimTypes.Surname)
?? principal.FindFirstValue("family_name")
?? string.Empty;View on GitHub (pinned to 3f2959e683)