{"record":{"id":"2e3633783dc422ce","repo":"block/buzz","slug":"failed-to-bind-e","errorCode":null,"errorMessage":"Failed to bind {}: {e}","messagePattern":"Failed to bind (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-relay/src/main.rs","lineNumber":1332,"sourceCode":"        // retains ownership of every delayed close until its 1012 frame has\n        // been flushed and acknowledged (or its send loop cancelled).\n        let closed = if drain_jitter_ms == 0 {\n            drain_conn_manager.drain_all()\n        } else {\n            drain_conn_manager.drain_all_jittered(drain_jitter_ms).await\n        };\n        info!(\n            connections = closed,\n            jitter_ms = drain_jitter_ms,\n            max_jitter_ms = MAX_DRAIN_JITTER_MS,\n            \"Signalled restart close to all live WebSocket connections\"\n        );\n        hard_shutdown_abort\n    });\n\n    let tcp_listener = tokio::net::TcpListener::bind(&config.bind_addr)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to bind {}: {e}\", config.bind_addr))?;\n    info!(addr = %config.bind_addr, \"buzz-relay TCP listening\");\n\n    #[cfg(unix)]\n    if let Some(ref uds_path) = config.uds_path {\n        use std::os::unix::fs::FileTypeExt as _;\n        match std::fs::symlink_metadata(uds_path) {\n            Ok(meta) if meta.file_type().is_socket() => {\n                let _ = std::fs::remove_file(uds_path);\n            }\n            Ok(_) => {\n                return Err(anyhow::anyhow!(\n                    \"BUZZ_UDS_PATH {uds_path} exists but is not a socket\"\n                ));\n            }\n            Err(_) => {}\n        }\n        let uds_listener = tokio::net::UnixListener::bind(uds_path)\n            .map_err(|e| anyhow::anyhow!(\"Failed to bind UDS {uds_path}: {e}\"))?;","sourceCodeStart":1314,"sourceCodeEnd":1350,"githubUrl":"https://github.com/block/buzz/blob/f956e6fe06a76e50cbd8fba1a162482e752e7f1a/crates/buzz-relay/src/main.rs#L1314-L1350","documentation":"serve() binds the main WebSocket/HTTP listener on config.bind_addr, which comes from BUZZ_BIND_ADDR (default 0.0.0.0:3000, config.rs:462-464); this error wraps the tokio TcpListener::bind io::Error. Address syntax problems are rejected earlier at config load (ConfigError 'invalid BUZZ_BIND_ADDR'), so reaching this line means the address parsed fine but the OS refused the bind — usually AddrInUse, AddrNotAvailable (IP not present on any interface), or PermissionDenied on privileged ports.","triggerScenarios":"Another process on :3000 (web/dev tooling, a second relay); BUZZ_BIND_ADDR naming a cluster-internal or stale static IP that does not exist on any local interface (EADDRNOTAVAIL); binding a port < 1024 without capabilities; host-networked replicas with identical BUZZ_BIND_ADDR.","commonSituations":"Local dev with node/vite/another relay already on 3000; k8s manifests setting BUZZ_BIND_ADDR to a Service IP instead of a pod-local address; migrating to a node where the previously bound static IP no longer exists.","solutions":["Check the holder: ss -ltnp 'sport = :3000' and stop it, or change BUZZ_BIND_ADDR to a free port.","If binding a specific IP, verify it exists on an interface (ip addr); otherwise bind 0.0.0.0 or fix the address.","Privileged ports require CAP_NET_BIND_SERVICE; prefer a port > 1024.","Give each co-located relay instance its own BUZZ_BIND_ADDR (and BUZZ_HEALTH_PORT)."],"exampleFix":"# before: Error: Failed to bind 0.0.0.0:3000: Address already in use (os error 98)\nBUZZ_BIND_ADDR=0.0.0.0:3000\n\n# after\nBUZZ_BIND_ADDR=0.0.0.0:3001","handlingStrategy":"validation","validationCode":"// Pre-flight: confirm the main bind address is bindable before boot.\nfn bind_addr_available(addr: std::net::SocketAddr) -> bool {\n    std::net::TcpListener::bind(addr).is_ok()\n}\n\nlet addr: std::net::SocketAddr = std::env::var(\"BUZZ_BIND_ADDR\")\n    .unwrap_or_else(|_| \"0.0.0.0:3000\".into())\n    .parse()\n    .expect(\"BUZZ_BIND_ADDR must parse\");\nassert!(bind_addr_available(addr), \"{addr} not bindable — in use, privileged, or not on an interface\");","typeGuard":"fn classify_bind_error(e: &std::io::Error) -> &'static str {\n    match e.kind() {\n        std::io::ErrorKind::AddrInUse => \"port in use\",\n        std::io::ErrorKind::AddrNotAvailable => \"IP not on any interface\",\n        std::io::ErrorKind::PermissionDenied => \"privileged port without CAP_NET_BIND_SERVICE\",\n        _ => \"other\",\n    }\n}","tryCatchPattern":"let tcp_listener = match tokio::net::TcpListener::bind(&config.bind_addr).await {\n    Ok(l) => l,\n    Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {\n        return Err(anyhow!(\"{} in use — change BUZZ_BIND_ADDR or stop the holder\", config.bind_addr))\n    }\n    Err(e) => return Err(anyhow!(\"Failed to bind {}: {e}\", config.bind_addr)),\n};","preventionTips":["Give co-located services distinct BUZZ_BIND_ADDR/BUZZ_HEALTH_PORT pairs in env files","Verify any explicitly bound IP exists on an interface (ip addr) before deploying","Prefer ports > 1024 or grant CAP_NET_BIND_SERVICE in the container spec"],"tags":["rust","network","port-bind","startup","tokio","websocket"],"backgroundTag":"address-already-in-use","analyzedSha":"f956e6fe06a76e50cbd8fba1a162482e752e7f1a","analyzedAt":"2026-08-16T22:11:40.750Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}