{"record":{"id":"c36798f056735fef","repo":"MHSanaei/3x-ui","slug":"not-hysteria2","errorCode":null,"errorMessage":"not hysteria2","messagePattern":"not hysteria2","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/util/link/outbound.go","lineNumber":435,"sourceCode":"}\n\nfunc splitMethodPass(userInfo string) (string, string) {\n\tbefore, after, ok := strings.Cut(userInfo, \":\")\n\tif !ok {\n\t\treturn \"2022-blake3-aes-128-gcm\", userInfo // guess\n\t}\n\treturn before, after\n}\n\n// --- hysteria2 ---\n\nfunc parseHysteria2(link string) (*ParseResult, error) {\n\tu, err := url.Parse(link)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif u.Scheme != \"hysteria2\" && u.Scheme != \"hy2\" {\n\t\treturn nil, fmt.Errorf(\"not hysteria2\")\n\t}\n\tauth := u.User.Username()\n\thost := u.Hostname()\n\tport := defaultPort(u.Port(), 443)\n\tparams := u.Query()\n\n\tstream := map[string]any{\n\t\t\"network\":  \"hysteria\",\n\t\t\"security\": \"tls\",\n\t\t\"hysteriaSettings\": map[string]any{\n\t\t\t\"version\":        2,\n\t\t\t\"auth\":           auth,\n\t\t\t\"udpIdleTimeout\": 60,\n\t\t},\n\t\t\"tlsSettings\": map[string]any{\n\t\t\t\"serverName\":           params.Get(\"sni\"),\n\t\t\t\"alpn\":                 splitCommaOrDefault(params.Get(\"alpn\"), []string{\"h3\"}),\n\t\t\t\"fingerprint\":          params.Get(\"fp\"),","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/util/link/outbound.go#L417-L453","documentation":"Returned by parseHysteria2 when the parsed URL's scheme is neither 'hysteria2' nor 'hy2'. Like the vless/trojan guards, ParseOutbound only forwards links with those two prefixes, so this indicates a direct call to parseHysteria2 or a custom dispatcher routing a hysteria-variant scheme (hysteria:// v1, hy3://) into it.","triggerScenarios":"Routing hysteria v1 links (hysteria://) to parseHysteria2 — v1 has an entirely different param format; dispatching on substring 'hy' or 'hysteria' without the exact prefix; direct parser calls in tests or forks.","commonSituations":"Subscriptions mixing hysteria v1 and v2 entries; forks adding v1 support and reusing the v2 parser; prefix tables matching 'hy2' case-sensitively while a provider emits 'HY2://'.","solutions":["Dispatch on the exact prefixes 'hysteria2://' and 'hy2://' (case-normalized), and use ParseOutbound publicly.","Give hysteria v1 (hysteria://) its own parser — its query params (auth, upmbps, alpn) differ from v2.","Reject or skip v1 links in batch imports rather than routing them to the v2 parser."],"exampleFix":"// before\ncase strings.Contains(link, \"hysteria\"):\n    return parseHysteria2(link)\n\n// after\ncase strings.HasPrefix(link, \"hysteria2://\"), strings.HasPrefix(link, \"hy2://\"):\n    return parseHysteria2(link)","handlingStrategy":"validation","validationCode":"func isHysteria2Link(link string) bool {\n    u, err := url.Parse(strings.TrimSpace(link))\n    if err != nil { return false }\n    s := strings.ToLower(u.Scheme)\n    return s == \"hysteria2\" || s == \"hy2\"\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Route only hysteria2:// and hy2:// links to the v2 parser.","Give hysteria v1 links their own parser or reject them explicitly.","Case-normalize schemes before dispatch."],"tags":["parsing","proxy-links","hysteria2","invariant"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}