{"record":{"id":"7b5d74883b76c0d7","repo":"bitwarden/server","slug":"one-or-more-collections-are-already-governed-by-an","errorCode":null,"errorMessage":"One or more collections are already governed by another access rule.","messagePattern":"One or more collections are already governed by another access rule\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"bitwarden_license/src/Services/Pam/Services/AccessRuleWriteValidator.cs","lineNumber":87,"sourceCode":"        }\n\n        var collections = await _collectionRepository.GetManyByManyIdsAsync(distinctIds);\n        if (collections.Count != distinctIds.Count)\n        {\n            throw new BadRequestException(\"One or more collections could not be found.\");\n        }\n\n        if (collections.Any(c => c.OrganizationId != organizationId))\n        {\n            throw new BadRequestException(\"One or more collections do not belong to this organization.\");\n        }\n\n        // Deletes clear Collection.AccessRuleId and the FK forbids dangling links, so any set link points at an\n        // existing rule; only a link to a different rule is a conflict. A rule being created has no id, so for it\n        // any link at all conflicts.\n        if (collections.Any(c => c.AccessRuleId.HasValue && c.AccessRuleId != existingRuleId))\n        {\n            throw new BadRequestException(\"One or more collections are already governed by another access rule.\");\n        }\n\n        return distinctIds;\n    }\n}\n","sourceCodeStart":69,"sourceCodeEnd":93,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/bitwarden_license/src/Services/Pam/Services/AccessRuleWriteValidator.cs#L69-L93","documentation":"Thrown by AccessRuleWriteValidator.ValidateCollectionsAsync (line 87) when a target collection already has its AccessRuleId FK set to a different rule. The code comment explains: deletes clear AccessRuleId and the FK forbids dangling links, so any set link points at an existing rule. For an update, only a link to a different rule conflicts; for a create (existingRuleId null), any set link conflicts.","triggerScenarios":"Creating a rule (existingRuleId null) targeting a collection whose AccessRuleId is already set, or updating a rule targeting a collection whose AccessRuleId points to a different rule's Id.","commonSituations":"Trying to attach a collection already governed by another rule; reassigning a collection without first clearing or reassigning the old rule; UI not refreshing the collection's current governance state.","solutions":["Remove the collection from the other access rule first (which clears Collection.AccessRuleId), then retry.","Check each collection's AccessRuleId before submitting: skip or unassign any already linked to another rule.","If reassigning, update the existing governing rule rather than creating a new one targeting the same collection."],"exampleFix":"// before — target a collection already governed elsewhere\nawait _validator.ValidateAsync(orgId, rule, collectionIds);\n// after — filter out governed collections or clear their link first\nvar collections = await _collectionRepository.GetManyByManyIdsAsync(collectionIds);\nvar free = collections.Where(c => !c.AccessRuleId.HasValue || c.AccessRuleId == existingRuleId)\n                      .Select(c => c.Id);\nawait _validator.ValidateAsync(orgId, rule, free, existingRuleId);","handlingStrategy":"validation","validationCode":"var collections = await _collectionRepository.GetManyByManyIdsAsync(collectionIds.Distinct().ToList());\nvar conflicts = collections\n    .Where(c => c.AccessRuleId.HasValue && c.AccessRuleId != existingRuleId)\n    .Select(c => c.Id)\n    .ToList();\nif (conflicts.Count > 0)\n    return Conflict($\"Collections already governed: {string.Join(\", \", conflicts)}\");","typeGuard":null,"tryCatchPattern":"try { await _validator.ValidateAsync(orgId, rule, collectionIds, existingRuleId); }\ncatch (BadRequestException ex) when (ex.Message.Contains(\"already governed\"))\n{ /* prompt user: reassign or remove from other rule first */ }","preventionTips":["Display each collection's current governing rule in the UI before assignment.","When reassigning, clear the old rule's collection link before creating the new one.","Use optimistic locking or a transaction to prevent concurrent rule assignments to the same collection."],"tags":["pam","access-rule","collections","conflict","foreign-key","validation"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}