micro/go-micro · error
unable to bind to %s
Error message
unable to bind to %s
What it means
Listen probes ports on a host starting from a base port, retrying up to a max number of attempts. If every bind attempt fails it gives up and returns "unable to bind to <addr>". It means no port in the scanned range could be opened on that address.
Source
Thrown at internal/util/net/net.go:78
return nil, errors.New("unable to extract port range")
}
// range the ports
for port := min; port <= max; port++ {
// try bind to host:port
ln, err := fn(HostPort(host, port))
if err == nil {
return ln, nil
}
// hit max port
if port == max {
return nil, err
}
}
// why are we here?
return nil, fmt.Errorf("unable to bind to %s", addr)
}
// Proxy returns the proxy and the address if it exits.
func Proxy(service string, address []string) (string, []string, bool) {
var hasProxy bool
// get proxy. we parse out address if present
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
// default name
if prx == "service" {
prx = "go.micro.proxy"
address = nil
}
// check if its an address
if v := strings.Split(prx, ":"); len(v) > 1 {
address = []string{prx}
}View on GitHub (pinned to 24529f1404)
Solutions
- Check what occupies the range: lsof -i -P | grep LISTEN or ss -ltnp, and kill stale processes.
- Re-run the test/program to pick a currently free port (the scan is dynamic).
- Widen the scan range (raise max) or start from a higher base port to avoid privileged/contended ports.
- Ensure previous runs are not leaking listeners (defer listener.Close()) that exhaust the range.
Example fix
// before
ln, err := net.Listen("tcp", "127.0.0.1:0")
// after
ln, err := utilnet.Listen("tcp", "127.0.0.1:0") // scans for a free port
if err != nil {
return fmt.Errorf("no free port on localhost: %w", err)
}
defer ln.Close() Defensive patterns
Strategy: retry
Validate before calling
func portFree(addr string) bool {
ln, err := net.Listen("tcp", addr)
if err != nil {
return false
}
ln.Close()
return true
} Try / catch
ln, err := utilnet.Listen("tcp", base)
if err != nil {
if strings.Contains(err.Error(), "unable to bind") {
time.Sleep(500 * time.Millisecond)
ln, err = utilnet.Listen("tcp", base) // retry once
}
if err != nil {
return fmt.Errorf("port allocation failed: %w", err)
}
}
defer ln.Close() Prevention
- Always defer listener.Close() so tests don't leak ports and exhaust the scan range.
- Use port 0 (:0) where possible to let the OS pick a free port.
- In CI, clean up stale processes holding ports before running suites.
- Avoid hardcoding ports; always rely on the dynamic allocation helper.
When it happens
Trigger: Calling internal/util/net.Listen (used by getFreeLocalhostAddress, testPool, gRPC client setup) when all ports in the scanned range are already in use, or when the OS denies binding (permissions, address family mismatch, address in use).
Common situations: Busy CI machines where the port range is exhausted by leaked listeners from prior test runs; another process squatting on the localhost port range; running without permission to bind low ports when the range includes privileged ports.
Related errors
- tcp dial %s: %w
- [ERR] mdns: Failed to bind to any udp port
- API error: nil response
- ErrIPNotFound
- failed request
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/c601d47df11141af.
Report an issue: GitHub.