aspnetboilerplate/aspnetboilerplate · error · UserFriendlyException
InvalidTenancyName
Error message
InvalidTenancyName
What it means
AbpTenantManager.ValidateTenancyNameAsync checks the tenancy name against AbpTenant<TUser>.TenancyNameRegex (by default ^[a-zA-Z][a-zA-Z0-9_-]{1,31}$ i.e. must start with a letter, be 2-32 chars of letters/digits/underscore/dash). If the name doesn't match, it throws UserFriendlyException with the localized 'InvalidTenancyName' message. This is called from ValidateTenantAsync during CreateAsync/UpdateAsync.
Solutions
- Validate the tenancy name against TenancyNameRegex before calling CreateAsync/UpdateAsync and return a field-level validation error.
- Normalize user input: trim, remove spaces/special characters, convert to the allowed charset.
- If your project overrides TenancyNameRegex, ensure the new pattern matches your existing tenants before deploying.
Example fix
// before
await _tenantManager.CreateAsync(new Tenant(userInput, name)); // throws for "my company!"
// after
var tenancyName = Regex.Replace(userInput.Trim(), "[^a-zA-Z0-9_-]", "");
if (!Regex.IsMatch(tenancyName, AbpTenant<CartItem>.TenancyNameRegex))
return BadRequest("Tenancy name must start with a letter and be 2-32 chars (letters, digits, - or _).");
await _tenantManager.CreateAsync(new Tenant(tenancyName, name)); Defensive patterns
Strategy: validation
Validate before calling
bool valid = Regex.IsMatch(tenancyName ?? "", AbpTenant<AbpUserBase>.TenancyNameRegex);
if (!valid) throw new UserFriendlyException("Invalid tenancy name: must start with a letter, 2-32 chars of letters/digits/-/_."); Type guard
bool IsValidTenancyName(string name) =>
!string.IsNullOrEmpty(name) && Regex.IsMatch(name, AbpTenant<AbpUserBase>.TenancyNameRegex); Try / catch
try { await _tenantManager.CreateAsync(tenant); }
catch (UserFriendlyException ex) when (ex.Message == _l("InvalidTenancyName")) { ModelState.AddModelError("TenancyName", ex.Message); return View(model); } Prevention
- Enforce TenancyNameRegex client-side in sign-up forms
- Sanitize/normalize names before validation
- Review custom TenancyNameRegex overrides before upgrading ABP
When it happens
Trigger: Creating or updating a tenant (CreateAsync/UpdateAsync → ValidateTenantAsync) whose TenancyName fails the regex: empty string, contains spaces or special characters, starts with a digit, or is longer than 32 characters.
Common situations: Sign-up forms that don't validate the tenant name client-side (allowing spaces or dots like 'my.company'); tenants named with uppercase/convention-violating input; customized TenancyNameRegex in a newer version breaking previously valid names; white-label imports with legacy naming.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Can not update a host language from tenant
- Identity.DuplicateEmail
- Identity.DuplicateUserName
- OrganizationUnitDuplicateDisplayNameWarning
- RoleDisplayNameIsAlreadyTaken
AI-assisted analysis of aspnetboilerplate/aspnetboilerplate@2323c13a15 (2026-09-08).
Data as JSON: /api/errors/19381b7c15bea74f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Abp.Zero.Common/MultiTenancy/AbpTenantManager.cs:428
}
});
}
protected virtual async Task ValidateTenantAsync(TTenant tenant)
{
await ValidateTenancyNameAsync(tenant.TenancyName);
}
protected virtual void ValidateTenant(TTenant tenant)
{
ValidateTenancyName(tenant.TenancyName);
}
protected virtual Task ValidateTenancyNameAsync(string tenancyName)
{
if (!Regex.IsMatch(tenancyName, AbpTenant<TUser>.TenancyNameRegex))
{
throw new UserFriendlyException(L("InvalidTenancyName"));
}
return Task.FromResult(0);
}
protected virtual void ValidateTenancyName(string tenancyName)
{
if (!Regex.IsMatch(tenancyName, AbpTenant<TUser>.TenancyNameRegex))
{
throw new UserFriendlyException(L("InvalidTenancyName"));
}
}
protected virtual string L(string name)
{
return LocalizationManager.GetString(LocalizationSourceName, name);
}
View on GitHub (pinned to 2323c13a15)