grpc/grpc-go · error

missing address

Error message

missing address

What it means

parseTarget in the delegating resolver (delegatingresolver.go:229-232) requires a non-empty address. When the endpoint extracted from the dial target is the empty string, the function returns this error. This path is reached for "dns" scheme targets with proxy-default-port handling enabled.

Solutions

  1. Provide a non-empty host (and optional port) in the dial target: "dns:///my-svc:443".
  2. Validate that service-discovery code returns a non-empty address before constructing the dial target.
  3. If a literal empty address is genuinely invalid in your context, surface it to the caller rather than passing it to gRPC.

Example fix

// before:
//   conn, err := grpc.Dial("dns:///", opts)

// after:
//   conn, err := grpc.Dial("dns:///"+hostname+":443", opts)
Defensive patterns

Strategy: validation

Validate before calling

package main

import (
	"fmt"
	"strings"
)

func nonEmptyEndpoint(ep string) (string, error) {
	ep = strings.TrimSpace(ep)
	if ep == "" {
		return "", fmt.Errorf("dial endpoint is empty")
	}
	return ep, nil
}

// func main() { _, _ = nonEmptyEndpoint("") }

Try / catch

// Dial returns this synchronously inside the delegating-resolver parseTarget.
//
//   conn, err := grpc.Dial(target, opts)
//   if err != nil && strings.Contains(err.Error(), "missing address") {
//       return fmt.Errorf("no dial target provided")
//   }

Prevention

When it happens

Trigger: Triggered inside delegatingresolver.parseTarget when target == "", called from New() when scheme is "dns", targetResolutionEnabled is false, and EnableDefaultPortForProxyTarget is true and the endpoint is empty (e.g. "dns:///").

Common situations: Dial target is "dns:///" or the endpoint was stripped to empty by upstream parsing; a misconfigured load-balancer/address-builder produced an empty address; a typo omitted the hostname.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/1e60d8d2c2af1434. Report an issue: GitHub.

Appendix: source

Thrown at internal/resolver/delegatingresolver/delegatingresolver.go:231

	}
	return false
}

// parseTarget takes a target string and ensures it is a valid "host:port" target.
//
// It does the following:
//  1. If the target already has a port (e.g., "host:port", "[ipv6]:port"),
//     it is returned as is.
//  2. If the host part is empty (e.g., ":80"), it defaults to "localhost",
//     returning "localhost:80".
//  3. If the target is missing a port (e.g., "host", "ipv6"), the defaultPort
//     is added.
//
// An error is returned for empty targets or targets with a trailing colon
// but no port (e.g., "host:").
func parseTarget(target string) (string, error) {
	if target == "" {
		return "", fmt.Errorf("missing address")
	}

	host, port, err := net.SplitHostPort(target)
	if err != nil {
		// If SplitHostPort fails, it's likely because the port is missing.
		// We append the default port and return the result.
		return net.JoinHostPort(target, defaultPort), nil
	}

	// If SplitHostPort succeeds, we check for edge cases.
	if port == "" {
		// A success with an empty port means the target had a trailing colon,
		// e.g., "host:", which is an error.
		return "", fmt.Errorf("missing port after port-separator colon")
	}
	if host == "" {
		// A success with an empty host means the target was like ":80".
		// We default the host to "localhost".

View on GitHub (pinned to 0c51461d27)