{"record":{"id":"6c83f8a942fc5075","repo":"grpc/grpc-go","slug":"passthrough-received-empty-target-in-build","errorCode":null,"errorMessage":"passthrough: received empty target in Build()","messagePattern":"passthrough: received empty target in Build\\(\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/resolver/passthrough/passthrough.go","lineNumber":35,"sourceCode":" */\n\n// Package passthrough implements a pass-through resolver. It sends the target\n// name without scheme back to gRPC as resolved address.\npackage passthrough\n\nimport (\n\t\"errors\"\n\n\t\"google.golang.org/grpc/resolver\"\n)\n\nconst scheme = \"passthrough\"\n\ntype passthroughBuilder struct{}\n\nfunc (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {\n\tif target.Endpoint() == \"\" && opts.Dialer == nil {\n\t\treturn nil, errors.New(\"passthrough: received empty target in Build()\")\n\t}\n\tr := &passthroughResolver{\n\t\ttarget: target,\n\t\tcc:     cc,\n\t}\n\tr.start()\n\treturn r, nil\n}\n\nfunc (*passthroughBuilder) Scheme() string {\n\treturn scheme\n}\n\ntype passthroughResolver struct {\n\ttarget resolver.Target\n\tcc     resolver.ClientConn\n}\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/resolver/passthrough/passthrough.go#L17-L53","documentation":"Returned by the passthrough resolver's Build method when target.Endpoint() is empty and no custom dialer is set. The passthrough resolver simply passes the target endpoint as the resolved address; if the endpoint is empty there is nothing to connect to, and without a custom dialer there is no way to handle the emptiness. This is the fallback resolver used when no scheme-specific resolver matches.","triggerScenarios":"grpc.Dial with an empty target (\"\") or a target that reduces to an empty endpoint after parsing (e.g., \"passthrough:///\"  with nothing after the authority), when no custom dialer (grpc.WithContextDialer) is configured. The check at passthrough.go:34-35 fires.","commonSituations":"Unset or empty address configuration; target string that is only a scheme with no endpoint; dial target computed from a broken template or missing environment variable; the passthrough scheme used explicitly with an empty address.","solutions":["Provide a non-empty endpoint in the dial target, e.g., grpc.Dial(\"passthrough:///10.0.0.1:8080\").","Validate the address string is non-empty before calling grpc.Dial.","If using a custom dialer that resolves the address itself, pass it via grpc.WithContextDialer (the error is suppressed when opts.Dialer != nil).","Log the target before dialing to catch empty values."],"exampleFix":"// before\nconn, err := grpc.Dial(\"\") // empty target\n// after\nconn, err := grpc.Dial(\"passthrough:///10.0.0.1:8080\")\n\n// or if using a custom dialer:\nconn, err := grpc.Dial(\"\",\n    grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {\n        return net.Dial(\"tcp\", \"10.0.0.1:8080\")\n    }),\n)","handlingStrategy":"validation","validationCode":"// Validate the target endpoint is non-empty before dialing.\ntarget := strings.TrimSpace(addr)\nif target == \"\" {\n    return fmt.Errorf(\"dial target is empty\")\n}\nconn, err := grpc.Dial(target)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate dial targets are non-empty before calling grpc.Dial.","If using a custom dialer, pass it via grpc.WithContextDialer to handle empty targets.","Fail fast at startup on empty address configuration.","Log the dial target to catch empty or malformed values."],"tags":["resolver","passthrough","dial-target","validation","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}