grpc/grpc-go · error

dns resolver: missing address

Error message

dns resolver: missing address

What it means

ErrMissingAddr is returned by the DNS resolver builder when the target address is empty. The DNS resolver needs a hostname to resolve; an empty string cannot be looked up. This error is defined in internal/resolver/dns/internal/internal.go and checked at the start of the resolver's Build function.

Solutions

  1. Provide a non-empty hostname in the dial target: grpc.Dial("dns:///my-service:8080").
  2. Validate the address is non-empty before dialing.
  3. Check environment variables or config files that supply the target are set and non-empty.
  4. Log the resolved target string to catch empty values early.

Example fix

// before
conn, err := grpc.Dial(os.Getenv("GRPC_TARGET")) // empty if unset
// after
target := os.Getenv("GRPC_TARGET")
if target == "" {
    log.Fatal("GRPC_TARGET must be set")
}
conn, err := grpc.Dial(target)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the dial target is non-empty before dialing.
target := os.Getenv("GRPC_TARGET")
if strings.TrimSpace(target) == "" {
    log.Fatal("dial target is empty; set GRPC_TARGET")
}
conn, err := grpc.Dial(target)

Prevention

When it happens

Trigger: Dialing gRPC with a dns:// scheme (or default resolver) where the target is empty: grpc.Dial("") or grpc.Dial("dns:///") . Also triggered when the target string is reduced to empty after scheme/authority stripping.

Common situations: Empty or uninitialized target variable passed to grpc.Dial; misconfigured service discovery that produces an empty address; environment variable for the server address unset, resulting in an empty dial target; DNS URI like 'dns:///' with no host.

Related errors


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

Appendix: source

Thrown at internal/resolver/dns/internal/internal.go:41

	"context"
	"errors"
	"net"
	"time"
)

// NetResolver groups the methods on net.Resolver that are used by the DNS
// resolver implementation. This allows the default net.Resolver instance to be
// overridden from tests.
type NetResolver interface {
	LookupHost(ctx context.Context, host string) (addrs []string, err error)
	LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error)
	LookupTXT(ctx context.Context, name string) (txts []string, err error)
}

var (
	// ErrMissingAddr is the error returned when building a DNS resolver when
	// the provided target name is empty.
	ErrMissingAddr = errors.New("dns resolver: missing address")

	// ErrEndsWithColon is the error returned when building a DNS resolver when
	// the provided target name ends with a colon that is supposed to be the
	// separator between host and port.  E.g. "::" is a valid address as it is
	// an IPv6 address (host only) and "[::]:" is invalid as it ends with a
	// colon as the host and port separator
	ErrEndsWithColon = errors.New("dns resolver: missing port after port-separator colon")
)

// The following vars are overridden from tests.
var (
	// TimeAfterFunc is used by the DNS resolver to wait for the given duration
	// to elapse. In non-test code, this is implemented by time.After. In test
	// code, this can be used to control the amount of time the resolver is
	// blocked waiting for the duration to elapse.
	TimeAfterFunc func(time.Duration) <-chan time.Time

	// TimeNowFunc is used by the DNS resolver to get the current time.

View on GitHub (pinned to 0c51461d27)