grpc/grpc-go · error
passthrough: received empty target in Build()
Error message
passthrough: received empty target in Build()
What it means
Returned by passthroughBuilder.Build (internal/resolver/passthrough/passthrough.go:35) when target.Endpoint() is empty AND opts.Dialer is nil. The passthrough resolver hands the endpoint straight to gRPC as the address; with no endpoint and no custom dialer there is nothing to dial, so Build fails. Note the condition: a custom dialer makes an empty endpoint acceptable (the dialer decides where to connect).
Source
Thrown at internal/resolver/passthrough/passthrough.go:35
*/
// Package passthrough implements a pass-through resolver. It sends the target
// name without scheme back to gRPC as resolved address.
package passthrough
import (
"errors"
"google.golang.org/grpc/resolver"
)
const scheme = "passthrough"
type passthroughBuilder struct{}
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
if target.Endpoint() == "" && opts.Dialer == nil {
return nil, errors.New("passthrough: received empty target in Build()")
}
r := &passthroughResolver{
target: target,
cc: cc,
}
r.start()
return r, nil
}
func (*passthroughBuilder) Scheme() string {
return scheme
}
type passthroughResolver struct {
target resolver.Target
cc resolver.ClientConn
}
View on GitHub (pinned to 03255a9237)
Solutions
- Pass a non-empty target to grpc.Dial, e.g. grpc.Dial("passthrough:///backend.example.com:443").
- If you intentionally dial with an empty endpoint, supply a custom dialer via grpc.WithContextDialer (Build allows empty endpoint when opts.Dialer != nil).
- Validate the address string is non-empty before Dialing.
Example fix
// before
conn, _ := grpc.Dial("passthrough:///", ...) // err: received empty target
// after
addr := os.Getenv("BACKEND_ADDR")
if addr == "" { log.Fatal("BACKEND_ADDR not set") }
conn, err := grpc.Dial("passthrough:///"+addr, ...)
// alternate: custom dialer with empty endpoint
// conn, _ := grpc.Dial("passthrough:///", grpc.WithContextDialer(myDialer)) Defensive patterns
Strategy: validation
Validate before calling
endpoint := strings.TrimSpace(rawTarget)
if endpoint == "" && customDialer == nil {
return errors.New("dial target is empty; provide an address or a custom dialer")
}
dialTarget := "passthrough:///" + endpoint Try / catch
conn, err := grpc.Dial(target, ...)
if err != nil {
if strings.Contains(err.Error(), "passthrough: received empty target") {
log.Fatal("dial target empty; set BACKEND_ADDR or pass a dialer")
}
log.Fatal(err)
} Prevention
- Validate the dial address is non-empty before calling grpc.Dial.
- If you must dial an empty target, supply grpc.WithContextDialer so Build accepts it.
When it happens
Trigger: Dialing with the passthrough scheme (or default scheme) where the target string has no endpoint, e.g. grpc.Dial("passthrough:///") or grpc.Dial("") without a WithContextDialer option. The resolver Build() runs during Dial and returns this error.
Common situations: Empty target string passed to grpc.Dial; building a target programmatically and the host portion is empty; using the default resolver (which falls back to passthrough) with a malformed address; expecting an env var to fill the address but it is unset.
Related errors
- dns resolver: missing address
- dns resolver: missing port after port-separator colon
- empty token_exchange_service_uri in options
- required field SubjectTokenPath is not specified
- required field SubjectTokenType is not specified
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/6c83f8a942fc5075.
Report an issue: GitHub.