{"record":{"id":"c601d47df11141af","repo":"micro/go-micro","slug":"unable-to-bind-to-s","errorCode":null,"errorMessage":"unable to bind to %s","messagePattern":"unable to bind to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/util/net/net.go","lineNumber":78,"sourceCode":"\t\treturn nil, errors.New(\"unable to extract port range\")\n\t}\n\n\t// range the ports\n\tfor port := min; port <= max; port++ {\n\t\t// try bind to host:port\n\t\tln, err := fn(HostPort(host, port))\n\t\tif err == nil {\n\t\t\treturn ln, nil\n\t\t}\n\n\t\t// hit max port\n\t\tif port == max {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// why are we here?\n\treturn nil, fmt.Errorf(\"unable to bind to %s\", addr)\n}\n\n// Proxy returns the proxy and the address if it exits.\nfunc Proxy(service string, address []string) (string, []string, bool) {\n\tvar hasProxy bool\n\n\t// get proxy. we parse out address if present\n\tif prx := os.Getenv(\"MICRO_PROXY\"); len(prx) > 0 {\n\t\t// default name\n\t\tif prx == \"service\" {\n\t\t\tprx = \"go.micro.proxy\"\n\t\t\taddress = nil\n\t\t}\n\n\t\t// check if its an address\n\t\tif v := strings.Split(prx, \":\"); len(v) > 1 {\n\t\t\taddress = []string{prx}\n\t\t}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/internal/util/net/net.go#L60-L96","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\n// after\nln, err := utilnet.Listen(\"tcp\", \"127.0.0.1:0\") // scans for a free port\nif err != nil {\n    return fmt.Errorf(\"no free port on localhost: %w\", err)\n}\ndefer ln.Close()","handlingStrategy":"retry","validationCode":"func portFree(addr string) bool {\n    ln, err := net.Listen(\"tcp\", addr)\n    if err != nil {\n        return false\n    }\n    ln.Close()\n    return true\n}","typeGuard":null,"tryCatchPattern":"ln, err := utilnet.Listen(\"tcp\", base)\nif err != nil {\n    if strings.Contains(err.Error(), \"unable to bind\") {\n        time.Sleep(500 * time.Millisecond)\n        ln, err = utilnet.Listen(\"tcp\", base) // retry once\n    }\n    if err != nil {\n        return fmt.Errorf(\"port allocation failed: %w\", err)\n    }\n}\ndefer ln.Close()","preventionTips":["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."],"tags":["network","bind","port-in-use","tcp"],"backgroundTag":"address-already-in-use","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}