{"record":{"id":"98a0f2669d9f761e","repo":"grpc/grpc-go","slug":"invalid-target-address-v-error-info-v","errorCode":null,"errorMessage":"invalid target address %v, error info: %v","messagePattern":"invalid target address (.+?), error info: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/resolver/dns/dns_resolver.go","lineNumber":416,"sourceCode":"\tif host, port, err = net.SplitHostPort(target); err == nil {\n\t\tif port == \"\" {\n\t\t\t// If the port field is empty (target ends with colon), e.g. \"[::1]:\",\n\t\t\t// this is an error.\n\t\t\treturn \"\", \"\", internal.ErrEndsWithColon\n\t\t}\n\t\t// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port\n\t\tif host == \"\" {\n\t\t\t// Keep consistent with net.Dial(): If the host is empty, as in \":80\",\n\t\t\t// the local system is assumed.\n\t\t\thost = \"localhost\"\n\t\t}\n\t\treturn host, port, nil\n\t}\n\tif host, port, err = net.SplitHostPort(target + \":\" + defaultPort); err == nil {\n\t\t// target doesn't have port\n\t\treturn host, port, nil\n\t}\n\treturn \"\", \"\", fmt.Errorf(\"invalid target address %v, error info: %v\", target, err)\n}\n\ntype rawChoice struct {\n\tClientLanguage *[]string        `json:\"clientLanguage,omitempty\"`\n\tPercentage     *int             `json:\"percentage,omitempty\"`\n\tClientHostName *[]string        `json:\"clientHostName,omitempty\"`\n\tServiceConfig  *json.RawMessage `json:\"serviceConfig,omitempty\"`\n}\n\nfunc containsString(a *[]string, b string) bool {\n\tif a == nil {\n\t\treturn true\n\t}\n\tfor _, c := range *a {\n\t\tif c == b {\n\t\t\treturn true\n\t\t}\n\t}","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/resolver/dns/dns_resolver.go#L398-L434","documentation":"The dns resolver's parseTarget (dns_resolver.go:390-417) tries several strategies: empty target -> ErrMissingAddr; pure IP -> returned as-is; SplitHostPort success -> host/port; otherwise it appends the default port and retries SplitHostPort. If even that final attempt fails, the target is unparseable and this error is returned. This is the dns scheme's own parser, distinct from the delegating resolver's parseTarget.","triggerScenarios":"Triggered when the dns resolver's parseTarget cannot make sense of the target string by any strategy, e.g. a target with unbalanced brackets, embedded illegal characters, or a malformed IPv6 literal.","commonSituations":"A dial target with a malformed IPv6 address (missing/extra brackets), stray characters, or a target that was incorrectly assembled by configuration code.","solutions":["Use a well-formed target: \"dns:///host:443\", \"dns:///host\", \"dns:///[::1]:443\", or a plain hostname.","For IPv6, wrap the address in brackets when a port is present: \"[2001:db8::1]:443\".","Log the exact target string passed to grpc.Dial and validate it before dialing."],"exampleFix":"// before:\n//   conn, err := grpc.Dial(\"dns:///::1:443\", opts) // malformed IPv6\n\n// after:\n//   conn, err := grpc.Dial(\"dns:///[::1]:443\", opts)","handlingStrategy":"validation","validationCode":"package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strings\"\n)\n\n// validateDialTarget mirrors the dns resolver's parseTarget rules so the error\n// is caught before grpc.Dial.\nfunc validateDnsTarget(target, defaultPort string) (string, string, error) {\n\tif target == \"\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"missing target\")\n\t}\n\tif _, err := netip.ParseAddr(target); err == nil {\n\t\treturn target, defaultPort, nil\n\t}\n\tif h, p, err := net.SplitHostPort(target); err == nil {\n\t\tif p == \"\" {\n\t\t\treturn \"\", \"\", fmt.Errorf(\"target ends with colon, missing port\")\n\t\t}\n\t\tif h == \"\" {\n\t\t\th = \"localhost\"\n\t\t}\n\t\treturn h, p, nil\n\t}\n\tif h, p, err := net.SplitHostPort(target + \":\" + defaultPort); err == nil {\n\t\treturn h, p, nil\n\t}\n\treturn \"\", \"\", fmt.Errorf(\"invalid target %q\", target)\n}\n\nvar _ = strings.TrimSpace\n\n// func main() { _, _, _ = validateDnsTarget(\"[::1]:443\", \"443\") }","typeGuard":null,"tryCatchPattern":"// grpc.Dial returns this synchronously from the dns resolver's Build.\n//\n//   conn, err := grpc.Dial(\"dns:///\"+target, opts)\n//   if err != nil && strings.Contains(err.Error(), \"invalid target address\") {\n//       return fmt.Errorf(\"fix dial target: %w\", err)\n//   }","preventionTips":["Wrap IPv6 literals in brackets when a port is present.","Reject empty or whitespace-only targets at config-load time.","Unit-test target parsing with the same shapes the resolver expects."],"tags":["dns","resolver","addressing","validation","ipv6","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}