{"record":{"id":"f78775481a8078f5","repo":"XTLS/Xray-core","slug":"invalid-resolver-r","errorCode":null,"errorMessage":"invalid resolver  + r","messagePattern":"invalid resolver  \\+ r","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"infra/conf/transport_finalmask.go","lineNumber":713,"sourceCode":"type Xdns struct {\n\tDomain json.RawMessage `json:\"domain\"`\n\n\tDomains   []string `json:\"domains\"`\n\tResolvers []string `json:\"resolvers\"`\n}\n\nfunc (c *Xdns) Build() (proto.Message, error) {\n\tif c.Domain != nil {\n\t\treturn nil, errors.PrintRemovedFeatureError(\"domain\", \"domains(server) & resolvers(client)\")\n\t}\n\n\tif len(c.Domains) == 0 && len(c.Resolvers) == 0 {\n\t\treturn nil, errors.New(\"empty domains & empty resolvers\")\n\t}\n\n\tfor _, r := range c.Resolvers {\n\t\tif !strings.Contains(r, \"+udp://\") {\n\t\t\treturn nil, errors.New(\"invalid resolver \", r)\n\t\t}\n\t}\n\n\treturn &xdns.Config{\n\t\tDomains:   c.Domains,\n\t\tResolvers: c.Resolvers,\n\t}, nil\n}\n\ntype XMC struct {\n\tHostname string       `json:\"hostname\"`\n\tProfiles []XMCProfile `json:\"profiles\"`\n\tPassword string       `json:\"password\"`\n}\n\ntype XMCProfile struct {\n\t// Resolve the UUID by username, then request the session profile with\n\t// unsigned=false. Client and server must use the same signed profile.","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/infra/conf/transport_finalmask.go#L695-L731","documentation":"Thrown by Xdns.Build() for every entry in the 'resolvers' list that does not contain the '+udp://' marker. Resolvers must be encoded in a compound form like 'IP+udp://domain' — the +udp:// part selects UDP transport and carries the DNS name. Any other shape (bare IP, plain URL, tcp scheme) is rejected.","triggerScenarios":"Writing \"8.8.8.8\" or \"https://dns.google\" or \"1.1.1.1+tcp://example.com\" in the resolvers array triggers this at Build(). Only strings containing '+udp://' pass, e.g. \"1.1.1.1+udp://dns.google\".","commonSituations":"Assuming resolvers take plain host:port DNS addresses; migrating from another client that accepts bare IPs; typos like 'udp://' without the '+' or '+udp:/'.","solutions":["Rewrite each resolver as '<source-ip>+udp://<domain>', e.g. \"1.1.1.1+udp://cloudflare-dns.com\".","Verify the '+' is present and the marker is exactly '+udp://'.","If you only need domain routing, move entries to 'domains' instead."],"exampleFix":"// before\n\"resolvers\": [\"8.8.8.8\"]\n// after\n\"resolvers\": [\"8.8.8.8+udp://dns.google\"]","handlingStrategy":"validation","validationCode":"for _, r := range resolvers {\n    if !strings.Contains(r, \"+udp://\") {\n        return fmt.Errorf(\"resolver %q must look like '<ip>+udp://<domain>'\", r)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Standardize resolver strings as '<source-ip>+udp://<resolver-domain>' in templates.","Reject bare IPs at generator time, not at core start.","Add a unit test asserting every emitted resolver contains '+udp://'."],"tags":["go","xray","config","validation","dns"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}