{"record":{"id":"70539ab9383830ec","repo":"cloudflare/cloudflared","slug":"failed-to-bind-to-address-s-w","errorCode":null,"errorMessage":"failed to bind to address (%s): %w","messagePattern":"failed to bind to address \\((.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"metrics/metrics.go","lineNumber":137,"sourceCode":"\t\t\tlistener, err := listeners.Listen(\"tcp\", address)\n\t\t\tif err == nil {\n\t\t\t\treturn listener, nil\n\t\t\t}\n\t\t}\n\n\t\t// When no port is available then bind to a random one\n\t\tlistener, err := listeners.Listen(\"tcp\", laddr)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to listen to default metrics address: %w\", err)\n\t\t}\n\n\t\treturn listener, nil\n\t}\n\n\t// Explicitly got a local address then bind to it\n\tlistener, err := listeners.Listen(\"tcp\", laddr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to bind to address (%s): %w\", laddr, err)\n\t}\n\n\treturn listener, nil\n}\n\nfunc ServeMetrics(\n\tl net.Listener,\n\tctx context.Context,\n\tconfig Config,\n\tlog *zerolog.Logger,\n) (err error) {\n\tvar wg sync.WaitGroup\n\t// Metrics port is privileged, so no need for further access control\n\ttrace.AuthRequest = func(*http.Request) (bool, bool) { return true, true }\n\th := newMetricsHandler(config, log)\n\tserver := &http.Server{\n\t\tReadTimeout:  10 * time.Second,\n\t\tWriteTimeout: 10 * time.Second,","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/metrics/metrics.go#L119-L155","documentation":"Returned when cloudflared is given an explicit metrics listen address and listeners.Listen(\"tcp\", laddr) fails to bind it. The configured address is included in the message and the underlying net.OpError is wrapped. This is the explicit-address counterpart of the default-address bind failure.","triggerScenarios":"Calling ServeMetrics / StartMetricsServer with an explicit --metrics address that is already bound by another process, is malformed, resolves to an address not present on the host, or requires privileges (ports <1024).","commonSituations":"--metrics pointing at a port another daemon already uses; binding to a specific interface IP that no longer exists (DHCP change); typo in host causing resolver failure; running in a container without the IP; two services configured with the same metrics port in deployment manifests.","solutions":["Inspect the wrapped cause: 'address already in use' → free the port; 'cannot assign requested address' → fix the IP/host","Choose a free port and update --metrics (e.g. --metrics 127.0.0.1:33755)","Bind to 0.0.0.0 or localhost instead of a stale interface IP","Verify /etc/hosts or DNS resolves the hostname in the metrics address"],"exampleFix":"// before\ncloudflared tunnel run --metrics 127.0.0.1:20241  # already in use\n// after\ncloudflared tunnel run --metrics 127.0.0.1:20242  # verified free with ss -ltnp","handlingStrategy":"validation","validationCode":"func metricsAddrBindable(addr string) error {\n    ln, err := net.Listen(\"tcp\", addr)\n    if err != nil {\n        return fmt.Errorf(\"metrics address %s not bindable: %w\", addr, err)\n    }\n    return ln.Close()\n}\n// run before launching cloudflared with --metrics <addr>","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the address is bindable with ss -ltnp / a pre-flight net.Listen before deploy","Avoid ports <1024 for metrics without root or CAP_NET_BIND_SERVICE","Use 127.0.0.1 with distinct ports per instance; avoid interface IPs that can disappear on DHCP change"],"tags":["go","network","metrics","port-conflict"],"backgroundTag":"address-already-in-use","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}