bitwarden/server · error · BadRequestException
A rule with that name already exists.
Error message
A rule with that name already exists.
What it means
Thrown by AccessRuleWriteValidator.ValidateNameIsUniqueAsync (line 58) when creating or updating a PAM access rule whose Name case-insensitively matches another rule in the same organization. The check loads every sibling via _repository.GetManyByOrganizationIdAsync, then compares with StringComparison.OrdinalIgnoreCase, excluding the rule being updated by existingRuleId (null for creates means every sibling is compared).
Source
Thrown at bitwarden_license/src/Services/Pam/Services/AccessRuleWriteValidator.cs:58
}
var conditions = _conditionsValidator.Validate(rule.Conditions);
if (!conditions.IsValid)
{
throw new BadRequestException(conditions.Error!);
}
await ValidateNameIsUniqueAsync(organizationId, rule.Name, existingRuleId);
return await ValidateCollectionsAsync(organizationId, collectionIds, existingRuleId);
}
private async Task ValidateNameIsUniqueAsync(Guid organizationId, string name, Guid? existingRuleId)
{
var siblings = await _repository.GetManyByOrganizationIdAsync(organizationId);
if (siblings.Any(r => r.Id != existingRuleId && string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)))
{
throw new BadRequestException("A rule with that name already exists.");
}
}
private async Task<List<Guid>> ValidateCollectionsAsync(Guid organizationId, IEnumerable<Guid> collectionIds,
Guid? existingRuleId)
{
var distinctIds = collectionIds.Distinct().ToList();
if (distinctIds.Count == 0)
{
return distinctIds;
}
var collections = await _collectionRepository.GetManyByManyIdsAsync(distinctIds);
if (collections.Count != distinctIds.Count)
{
throw new BadRequestException("One or more collections could not be found.");
}
View on GitHub (pinned to e93b962371)
Solutions
- Use a distinct, organization-unique name for the rule before calling ValidateAsync.
- On update, always pass existingRuleId (the rule's own Id) so the validator excludes the rule being edited.
- Pre-fetch siblings via GetManyByOrganizationIdAsync and check for collisions client-side before submit.
- Normalize names (trim whitespace, enforce casing convention) before submission.
Example fix
// before — update without existingRuleId, own name looks like a duplicate await _validator.ValidateAsync(orgId, rule, collectionIds); // after — pass existingRuleId so the rule's own record is excluded await _validator.ValidateAsync(orgId, rule, collectionIds, existingRuleId: rule.Id);
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check name uniqueness before calling ValidateAsync
var siblings = await _repository.GetManyByOrganizationIdAsync(organizationId);
var isDuplicate = siblings.Any(r =>
r.Id != existingRuleId &&
string.Equals(r.Name, proposedName, StringComparison.OrdinalIgnoreCase));
if (isDuplicate)
return BadRequest($"A rule named '{proposedName}' already exists in this organization."); Try / catch
try { await _validator.ValidateAsync(orgId, rule, collectionIds, existingRuleId); }
catch (BadRequestException ex) when (ex.Message.Contains("already exists"))
{ /* return 409 Conflict or surface to user */ } Prevention
- Always pass existingRuleId on updates so the validator excludes the rule's own name.
- Enforce a naming convention (e.g., lowercase, no trailing whitespace) to reduce accidental case-variant collisions.
- Show existing rule names in the UI before the user submits to avoid duplicates.
When it happens
Trigger: Calling ValidateAsync with an AccessRule whose Name matches a sibling's Name (case-insensitive) where the sibling's Id != existingRuleId. Happens on both create (existingRuleId null) and update (existingRuleId omitted or wrong).
Common situations: Admin creating a rule named 'Prod' when 'prod' already exists; renaming during update and forgetting to pass existingRuleId so the validator sees the rule's own name as a collision; concurrent rule creation by two admins; test fixtures not cleaning up names.
Related errors
- One or more collections could not be found.
- One or more collections do not belong to this organization.
- One or more collections are already governed by another acce
- Name is required.
- A maximum extension length is required when extensions are a
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/73a8c400848e6ab6.
Report an issue: GitHub.