{"record":{"id":"ea34a2e93c8a1670","repo":"apache/superset","slug":"a-rule-with-this-name-already-exists-ea34a2","errorCode":null,"errorMessage":"A rule with this name already exists.","messagePattern":"A rule with this name already exists\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"superset/commands/security/update.py","lineNumber":59,"sourceCode":"    def __init__(self, model_id: int, data: dict[str, Any]):\n        self._model_id = model_id\n        self._properties = data.copy()\n        self._tables = self._properties.get(\"tables\", [])\n        self._subjects = self._properties.get(\"subjects\", [])\n        self._model: Optional[RowLevelSecurityFilter] = None\n\n    @transaction()\n    def run(self) -> Any:\n        self.validate()\n        assert self._model\n        try:\n            updated_model = RLSDAO.update(self._model, self._properties)\n            db.session.flush()\n        except IntegrityError as ex:\n            # The preflight uniqueness check in ``validate`` isn't atomic with\n            # this update, so fall back to the database's unique constraint\n            # and translate it into the same descriptive validation error.\n            raise ValidationError(\n                {\"name\": [_(\"A rule with this name already exists.\")]}\n            ) from ex\n        return updated_model\n\n    def validate(self) -> None:\n        self._model = RLSDAO.find_by_id(int(self._model_id))\n        if not self._model:\n            raise RLSRuleNotFoundError()\n\n        # Datasource access is validated before revealing whether the\n        # requested name is already in use, so an unauthorized caller can't\n        # use the duplicate-name response to enumerate rule names.\n        if \"tables\" in self._properties:\n            tables = (\n                db.session.query(SqlaTable)\n                .filter(SqlaTable.id.in_(self._tables))  # type: ignore[attr-defined]\n                .all()\n            )","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/security/update.py#L41-L77","documentation":"Raised by UpdateRLSRuleCommand.run when RLSDAO.update + db.session.flush() hits an IntegrityError on the rule-name unique constraint. The preflight validate_uniqueness in validate() is not atomic with the update, so concurrent renames can collide here; the constraint error is translated into a ValidationError on 'name'.","triggerScenarios":"PUT /api/v1/rowlevelsecurity/{id} renaming a rule to a name another session created (or renamed to) between the preflight check and the flush; two concurrent PUTs converging on the same name.","commonSituations":"Parallel automation renaming RLS rules; retry-after-timeout logic that re-sends a rename which another attempt already applied.","solutions":["On this 400, GET the rules list and reconcile: either pick a different name or update the other rule that now owns the name","Retry the PUT with a distinct name","Serialize rule renames through a single writer to avoid the race"],"exampleFix":"# before\nclient.put(f'/api/v1/rowlevelsecurity/{rid}', json={'name': 'tenant_filter'})\n\n# after\nname = 'tenant_filter'\nclash = client.get('/api/v1/rowlevelsecurity/?q=(name:eq:tenant_filter)').json()['count']\nif clash:\n    name = 'tenant_filter_v2'\nclient.put(f'/api/v1/rowlevelsecurity/{rid}', json={'name': name})","handlingStrategy":"try-catch","validationCode":"taken = any(r['name'] == new_name and r['id'] != rid\n            for r in client.get('/api/v1/rowlevelsecurity/').json()['result'])\nassert not taken","typeGuard":null,"tryCatchPattern":"from superset.commands.exceptions import ValidationError\ntry:\n    UpdateRLSRuleCommand(rid, props).run()\nexcept ValidationError as e:\n    if 'name' in e.normalized_messages():\n        props['name'] = uniquify(props['name']); UpdateRLSRuleCommand(rid, props).run()\n    else:\n        raise","preventionTips":["Check target-name availability (excluding own id) before rename","Serialize rule renames through one writer","Reconcile instead of blind-retrying after a timeout"],"tags":["rls","integrity-error","race-condition","rename"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}