{"record":{"id":"3dc017133a91d408","repo":"grpc/grpc-go","slug":"delegating-resolver-invalid-target-address-q-v","errorCode":null,"errorMessage":"delegating_resolver: invalid target address %q: %v","messagePattern":"delegating_resolver: invalid target address %q: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/resolver/delegatingresolver/delegatingresolver.go","lineNumber":119,"sourceCode":"//     in the target URI or specified by the user using the WithResolvers dial\n//     option. As a special case, if the target URI's scheme is \"dns\" and a\n//     proxy is specified using the supported environment variables, the target\n//     URI's path portion is used as the resolved address unless target\n//     resolution is enabled using the dial option.\nfunc New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions, targetResolverBuilder resolver.Builder, targetResolutionEnabled bool) (resolver.Resolver, error) {\n\tr := &delegatingResolver{\n\t\ttarget:         target,\n\t\tcc:             cc,\n\t\tproxyResolver:  nopResolver{},\n\t\ttargetResolver: nopResolver{},\n\t}\n\n\taddr := target.Endpoint()\n\tvar err error\n\tif target.URL.Scheme == \"dns\" && !targetResolutionEnabled && envconfig.EnableDefaultPortForProxyTarget {\n\t\taddr, err = parseTarget(addr)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"delegating_resolver: invalid target address %q: %v\", target.Endpoint(), err)\n\t\t}\n\t}\n\n\tr.proxyURL, err = proxyURLForTarget(addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"delegating_resolver: failed to determine proxy URL for target %q: %v\", target, err)\n\t}\n\n\t// proxy is not configured or proxy address excluded using `NO_PROXY` env\n\t// var, so only target resolver is used.\n\tif r.proxyURL == nil {\n\t\treturn targetResolverBuilder.Build(target, cc, opts)\n\t}\n\n\tif logger.V(2) {\n\t\tlogger.Infof(\"Proxy URL detected : %s\", r.proxyURL)\n\t}\n","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/resolver/delegatingresolver/delegatingresolver.go#L101-L137","documentation":"When the target URI uses the \"dns\" scheme, target resolution on the client is disabled, and envconfig.EnableDefaultPortForProxyTarget is true, the delegating resolver calls parseTarget(addr) on the endpoint to normalize it into host:port form (delegatingresolver.go:116-120). If parseTarget rejects the address (empty or trailing colon with no port), New() fails with this wrapped error instead of constructing the resolver.","triggerScenarios":"Triggered in delegatingresolver.New when target.URL.Scheme==\"dns\", targetResolutionEnabled is false, EnableDefaultPortForProxyTarget is true, and parseTarget(target.Endpoint()) returns an error — i.e. the endpoint is empty (\"missing address\") or ends with a colon but no port (\"missing port after port-separator colon\").","commonSituations":"A dial target like \"dns:///\" (no host), \"dns:///:\" or \"dns:///host:\" combined with a configured HTTPS_PROXY; the env var GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET (or default) is enabled; a service discovery layer produced an empty endpoint.","solutions":["Supply a complete target endpoint with a host and optional port, e.g. \"dns:///svc.cluster.local:443\" or \"dns:///svc.cluster.local\".","Remove any trailing colon (\"host:\") from the target string.","If you do not want the delegating resolver to pre-parse the address, enable target resolution on the client via the appropriate dial option so this code path is skipped.","Disable EnableDefaultPortForProxyTarget if your deployment does not require it."],"exampleFix":"// before:\n//   conn, err := grpc.Dial(\"dns:///host:\", opts)\n//   // with HTTPS_PROXY set\n\n// after:\n//   conn, err := grpc.Dial(\"dns:///host:443\", opts)","handlingStrategy":"validation","validationCode":"package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n)\n\n// normalizeDialEndpoint ensures the endpoint is a valid host[:port] before it\n// reaches the delegating resolver.\nfunc normalizeDialEndpoint(ep string) (string, error) {\n\tep = strings.TrimSpace(ep)\n\tif ep == \"\" {\n\t\treturn \"\", fmt.Errorf(\"missing address\")\n\t}\n\tif strings.HasSuffix(ep, \":\") {\n\t\treturn \"\", fmt.Errorf(\"missing port after colon in %q\", ep)\n\t}\n\tif _, _, err := net.SplitHostPort(ep); err == nil {\n\t\treturn ep, nil\n\t}\n\treturn net.JoinHostPort(ep, \"443\"), nil\n}\n\n// func main() { _, _ = normalizeDialEndpoint(\"host:\") }","typeGuard":null,"tryCatchPattern":"// Dial returns the error synchronously. Wrap it to give a clearer message.\n//\n//   conn, err := grpc.Dial(target, opts)\n//   if err != nil && strings.Contains(err.Error(), \"invalid target address\") {\n//       return fmt.Errorf(\"bad dial target %q: %w\", target, err)\n//   }","preventionTips":["Always include a host (and optional port) in dns: targets.","Avoid trailing colons in templated addresses.","Pre-validate dial targets with net.SplitHostPort before calling grpc.Dial."],"tags":["resolver","delegating-resolver","dns","proxy","addressing","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}