{"record":{"id":"dc7e60973cf60634","repo":"XTLS/Xray-core","slug":"empty-tag-name","errorCode":null,"errorMessage":"empty tag name!","messagePattern":"empty tag name!","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/router/router.go","lineNumber":228,"sourceCode":"\n// RemoveRule implements routing.Router.\nfunc (r *Router) RemoveRule(tag string) error {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tnewRules := []*Rule{}\n\tif tag != \"\" {\n\t\tfor _, rule := range r.rules {\n\t\t\tif rule.RuleTag != tag {\n\t\t\t\tnewRules = append(newRules, rule)\n\t\t\t} else if rule.Webhook != nil {\n\t\t\t\trule.Webhook.Close()\n\t\t\t}\n\t\t}\n\t\tr.rules = newRules\n\t\treturn nil\n\t}\n\treturn errors.New(\"empty tag name!\")\n}\n\n// ListRule implements routing.Router\nfunc (r *Router) ListRule() []routing.Route {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\truleList := make([]routing.Route, 0)\n\tfor _, rule := range r.rules {\n\t\truleList = append(ruleList, &Route{\n\t\t\toutboundTag: rule.Tag,\n\t\t\truleTag:     rule.RuleTag,\n\t\t})\n\t}\n\treturn ruleList\n}\n\nfunc (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {\n\t// SkipDNSResolve is set from DNS module.","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/router/router.go#L210-L246","documentation":"RemoveRule treats an empty tag as a programming error: the removal loop only runs when tag != \", so an empty string can never match anything and is rejected rather than silently succeeding. Tags are the sole selector for rule removal.","triggerScenarios":"Calling RemoveRule with an empty/zero-value string; protobuf field ruleTag left unset by client code.","commonSituations":"Clients reading ruleTag from a struct where the field was never populated; UI panels sending empty selections; default-initialized requests.","solutions":["Supply the exact ruleTag of an existing rule (obtainable from ListRule)","Guard client-side against empty tags before calling RemoveRule","Ensure rules you intend to manage dynamically are configured with ruleTag set"],"exampleFix":"// before\nr.RemoveRule(rule.GetRuleTag()) // rule had no ruleTag -> \"\"\n\n// after\n// configure rules with explicit ruleTag, then:\nr.RemoveRule(\"my-rule-tag\")","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(tag) == \"\" {\n    return fmt.Errorf(\"ruleTag required for removal\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Require ruleTag on every dynamically-managed rule at creation time","Populate removal UIs from ListRule output so tags are never hand-typed empty","Client-side guard empty selections before the RPC"],"tags":["routing","rule","validation","api"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}