grpc/grpc-go · error
missing port after port-separator colon
Error message
missing port after port-separator colon
What it means
In delegatingresolver.parseTarget (delegatingresolver.go:234-246), after net.SplitHostPort succeeds, an empty port indicates the target had a trailing colon but no port number (e.g. "host:"). The function rejects this because the port is ambiguous. An empty host with a port (":80") is allowed and defaults to localhost.
Solutions
- Add the port number: "dns:///host:443".
- If you intend the default port (443 for the proxy path), simply omit the colon entirely: "dns:///host".
- Fix the config/template that produced the trailing colon.
Example fix
// before:
// conn, err := grpc.Dial("dns:///host:", opts)
// after:
// conn, err := grpc.Dial("dns:///host:443", opts) Defensive patterns
Strategy: validation
Validate before calling
package main
import (
"fmt"
"strings"
)
func ensureHasPort(ep string) (string, error) {
if strings.HasSuffix(ep, ":") {
return "", fmt.Errorf("target %q ends with ':' but has no port", ep)
}
return ep, nil
}
// func main() { _, _ = ensureHasPort("host:") } Try / catch
// Dial surfaces this synchronously from the delegating resolver.
//
// conn, err := grpc.Dial(target, opts)
// if err != nil && strings.Contains(err.Error(), "missing port after port-separator") {
// return fmt.Errorf("fix trailing colon in %q", target)
// } Prevention
- Avoid templating that appends ':' unconditionally to hostnames.
- When a port is optional, omit the colon entirely rather than leaving it empty.
- Validate dial targets with net.SplitHostPort before dialing.
When it happens
Trigger: Triggered when the endpoint passed to parseTarget is of the form "host:" (trailing colon, no port digits), e.g. dial target "dns:///host:" under the proxy-default-port code path.
Common situations: A templating bug appends ':' to a hostname, a config field that should hold a port is empty producing "host:", or a copy-paste target omits the port number after the colon.
Related errors
- missing address
- delegating_resolver: invalid target address
- delegating_resolver: unable to build the resolver for target
- invalid target address
- delegating_resolver: failed to determine proxy URL for…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/aa36b556e874e511.
Report an issue: GitHub.
Appendix: source
Thrown at internal/resolver/delegatingresolver/delegatingresolver.go:245
// 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".
host = "localhost"
}
return net.JoinHostPort(host, port), nil
}
func skipProxy(address resolver.Address) bool {
// Avoid proxy when network is not tcp.
networkType, ok := networktype.Get(address)
if !ok {
networkType, _ = transport.ParseDialTarget(address.Addr)
}
if networkType != "tcp" {
return true
}View on GitHub (pinned to 0c51461d27)