{"record":{"id":"2799411805db527f","repo":"XTLS/Xray-core","slug":"unsupported-getprincipletarget","errorCode":null,"errorMessage":"unsupported GetPrincipleTarget","messagePattern":"unsupported GetPrincipleTarget","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/router/balancing.go","lineNumber":147,"sourceCode":"\ths, ok := b.ohm.(outbound.HandlerSelector)\n\tif !ok {\n\t\treturn nil, errors.New(\"outbound.Manager is not a HandlerSelector\")\n\t}\n\ttags := hs.Select(b.selectors)\n\treturn tags, nil\n}\n\n// GetPrincipleTarget implements routing.BalancerPrincipleTarget\nfunc (r *Router) GetPrincipleTarget(tag string) ([]string, error) {\n\tif b, ok := r.balancers[tag]; ok {\n\t\tif s, ok := b.strategy.(BalancingPrincipleTarget); ok {\n\t\t\tcandidates, err := b.SelectOutbounds()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.New(\"unable to select outbounds\").Base(err)\n\t\t\t}\n\t\t\treturn s.GetPrincipleTarget(candidates), nil\n\t\t}\n\t\treturn nil, errors.New(\"unsupported GetPrincipleTarget\")\n\t}\n\treturn nil, errors.New(\"cannot find tag\")\n}\n\n// SetOverrideTarget implements routing.BalancerOverrider\nfunc (r *Router) SetOverrideTarget(tag, target string) error {\n\tif b, ok := r.balancers[tag]; ok {\n\t\tb.override.Put(target)\n\t\treturn nil\n\t}\n\treturn errors.New(\"cannot find tag\")\n}\n\n// GetOverrideTarget implements routing.BalancerOverrider\nfunc (r *Router) GetOverrideTarget(tag string) (string, error) {\n\tif b, ok := r.balancers[tag]; ok {\n\t\treturn b.override.Get(), nil\n\t}","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/router/balancing.go#L129-L165","documentation":"Router.GetPrincipleTarget was called with a valid balancer tag, but the balancer's strategy does not implement the BalancingPrincipleTarget interface (the optional GetPrincipleTarget(candidates) method). Only strategies that expose their decision rationale — such as built-in leastload/leastping styles that implement it — can answer; random or custom strategies cannot, so the router returns this unsupported-capability error.","triggerScenarios":"Calling the principle-target API for a balancer configured with \"strategy\" whose type is random (or a custom strategy) — the type assertion b.strategy.(BalancingPrincipleTarget) fails and the error is returned before any candidate work.","commonSituations":"Panels/tools that display 'current best node' querying every balancer, including ones using the random strategy; users switching a balancer from leastping to random and seeing the API query start failing while forwarding still works fine.","solutions":["Switch the balancer's strategy to one implementing principle-target reporting (e.g. leastPing/leastLoad) if the observation feature is needed.","Keep random balancers but have tooling expect and skip this error (capability probe pattern).","No forwarding impact — verify data path separately before spending time here."],"exampleFix":"// before\n\"balancers\": [ { \"tag\": \"bal\", \"selector\": [ \"node\" ], \"strategy\": { \"type\": \"random\" } } ]\n\n// after\n\"balancers\": [ { \"tag\": \"bal\", \"selector\": [ \"node\" ], \"strategy\": { \"type\": \"leastPing\" } } ]","handlingStrategy":"type-guard","validationCode":"null // runtime capability depends on the configured strategy type","typeGuard":"func strategyReportsPrincipleTarget(strategyType string) bool {\n    switch strategyType {\n    case \"leastPing\", \"leastLoad\", \"\": return true // defaults implement it\n    default: return false // e.g. random, custom\n    }\n}","tryCatchPattern":"if _, err := router.GetPrincipleTarget(tag); err != nil {\n    if strings.Contains(err.Error(), \"unsupported GetPrincipleTarget\") {\n        return nil, nil // capability gap, not an error condition for callers\n    }\n    return nil, err\n}","preventionTips":["Map which strategies implement principle-target before wiring dashboards","Switch to leastPing/leastLoad when 'current pick' visibility is required","Design panels to hide the metric gracefully for random/custom balancers"],"tags":["go","xray","routing","balancer","api"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}