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
- Provide a non-empty hostname in the dial target: grpc.Dial("dns:///my-service:8080").
- Validate the address is non-empty before dialing.
- Check environment variables or config files that supply the target are set and non-empty.
- 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
- Validate address strings are non-empty before passing to grpc.Dial.
- Set defaults for environment variables that supply dial targets.
- Log the resolved target to catch empty values early.
- Use config validation at startup to fail fast on empty addresses.
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
- dns resolver: missing port after port-separator colon
- invalid target address
- passthrough: received empty target in Build()
- delegating_resolver: invalid target address
- delegating_resolver: unable to build the proxy resolver
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)