{"record":{"id":"3b1fdae3b30a16db","repo":"XTLS/Xray-core","slug":"duplicate-ruletag-s","errorCode":null,"errorMessage":"duplicate ruleTag %s","messagePattern":"duplicate ruleTag (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/router/router.go","lineNumber":162,"sourceCode":"\t\t}\n\t\tbalancer.InjectContext(r.ctx)\n\t\tr.balancers[rule.Tag] = balancer\n\t}\n\n\tstartIdx := len(r.rules)\n\tcloseNewWebhooks := func() {\n\t\tfor i := startIdx; i < len(r.rules); i++ {\n\t\t\tif r.rules[i].Webhook != nil {\n\t\t\t\tr.rules[i].Webhook.Close()\n\t\t\t}\n\t\t}\n\t\tr.rules = r.rules[:startIdx]\n\t}\n\n\tfor _, rule := range config.Rule {\n\t\tif r.RuleExists(rule.GetRuleTag()) {\n\t\t\tcloseNewWebhooks()\n\t\t\treturn errors.New(\"duplicate ruleTag \", rule.GetRuleTag())\n\t\t}\n\t\tcond, err := rule.BuildCondition()\n\t\tif err != nil {\n\t\t\tcloseNewWebhooks()\n\t\t\treturn err\n\t\t}\n\t\trr := &Rule{\n\t\t\tCondition: cond,\n\t\t\tTag:       rule.GetTag(),\n\t\t\tRuleTag:   rule.GetRuleTag(),\n\t\t}\n\t\tif wh := rule.GetWebhook(); wh != nil {\n\t\t\tnotifier, err := NewWebhookNotifier(wh)\n\t\t\tif err != nil {\n\t\t\t\tcloseNewWebhooks()\n\t\t\t\treturn err\n\t\t\t}\n\t\t\trr.Webhook = notifier","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/router/router.go#L144-L180","documentation":"A rule being added (initial load or reload) has a ruleTag that already exists in r.rules. Rule tags are the handle for RemoveRule/override operations, so uniqueness is enforced; on failure newly appended rules and opened webhooks are rolled back.","triggerScenarios":"ReloadRules with shouldAppend=true re-submitting a rule whose ruleTag is already registered; initial config declaring the same ruleTag twice.","commonSituations":"Panels re-adding rules without checking RuleExists first; config merges producing duplicated entries.","solutions":["Remove or rename the conflicting ruleTag before adding","Call ListRule/RuleExists first and skip or update instead of duplicating","If replacing, send shouldAppend=false to rebuild the full rule set"],"exampleFix":"// before\nAddRule(configWithRuleTag(\"rule-1\"), true) // rule-1 already exists\n\n// after\nif r.RuleExists(\"rule-1\") { r.RemoveRule(\"rule-1\") }\nAddRule(configWithRuleTag(\"rule-1\"), true)","handlingStrategy":"validation","validationCode":"for _, r := range cfg.Rule {\n    if existing.RuleExists(r.GetRuleTag()) {\n        return fmt.Errorf(\"ruleTag %q already present; remove or rename first\", r.GetRuleTag())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call RuleExists/ListRule before AddRule in append mode","Generate unique ruleTags (e.g. domain-hash or uuid) when creating rules programmatically","Replace-by-tag flow: RemoveRule then AddRule in one transaction"],"tags":["routing","rule","configuration","duplicate"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}