bitwarden/server · error · BadRequestException

Resources must be unique

Error message

Resources must be unique

What it means

Thrown by AccessPolicyHelpers.CheckForDistinctAccessPolicies when the number of input access policies does not equal the number of distinct policies after de-duplication by (granteeId, grantedResourceId) tuple. This means the request contains duplicate access policy entries — the same user/group is granted access to the same project/secret/service-account more than once.

Source

Thrown at src/Api/SecretsManager/Utilities/AccessPolicyHelpers.cs:32

            {
                UserProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedProjectId),
                UserSecretAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedSecretId),
                UserServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId,
                    ap.GrantedServiceAccountId),
                GroupProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedProjectId),
                GroupSecretAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedSecretId),
                GroupServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedServiceAccountId),
                ServiceAccountProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId,
                    ap.GrantedProjectId),
                ServiceAccountSecretAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId,
                    ap.GrantedSecretId),
                _ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)),
            };
        }).ToList();

        if (accessPolicies.Count != distinctAccessPolicies.Count)
        {
            throw new BadRequestException("Resources must be unique");
        }
    }

    public static void CheckAccessPoliciesHaveReadPermission(IEnumerable<BaseAccessPolicy> accessPolicies)
    {
        var accessPoliciesPermission = accessPolicies.All(policy => policy.Read);
        if (!accessPoliciesPermission)
        {
            throw new BadRequestException("Resources must be Read = true");
        }
    }
}

View on GitHub (pinned to e93b962371)

Solutions

  1. De-duplicate the access policy list by (granteeId, grantedResourceId) before submitting.
  2. Inspect the request payload for repeated entries and remove duplicates.
  3. If using a UI, ensure the add-access form prevents adding the same user/group twice for the same resource.

Example fix

// before: duplicate user+project pair
var policies = new[] {
  new { OrgUserId = uid, GrantedProjectId = pid, Read = true },
  new { OrgUserId = uid, GrantedProjectId = pid, Read = true }  // duplicate
};
// after: distinct pairs only
var policies = new[] {
  new { OrgUserId = uid, GrantedProjectId = pid, Read = true }
};
Defensive patterns

Strategy: validation

Validate before calling

// De-duplicate access policies by (grantee, granted) before submission
var distinct = policies
    .GroupBy(p => GetPolicyKey(p))
    .Select(g => g.First())
    .ToList();
// GetPolicyKey returns (granteeId, grantedResourceId) per policy type
await client.SetAccessPoliciesAsync(distinct);

Type guard

static bool HasDistinctAccessPolicies(IEnumerable<BaseAccessPolicy> policies)
    => policies.Count() == policies.DistinctBy(GetPolicyKey).Count();

Try / catch

try { await client.SetAccessPoliciesAsync(policies); }
catch (ApiException ex) when (ex.Message.Contains("must be unique"))
{
    var deduped = policies.GroupBy(GetPolicyKey).Select(g => g.First()).ToList();
    await client.SetAccessPoliciesAsync(deduped);
}

Prevention

When it happens

Trigger: An access policy bulk-create/update request includes the same (organizationUserId, grantedProjectId) or (groupId, grantedSecretId) pair multiple times — e.g., the client sends two entries for the same user on the same project.

Common situations: Frontend form resubmits and appends duplicates; merging access policy lists from multiple sources without de-duplicating; copy-paste error in policy JSON.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/ada9ac4635c56016. Report an issue: GitHub.