block/buzz · critical · anyhow::Error

Failed to bind

Error message

Failed to bind {}: {e}

What it means

This is the bind of buzz-relay's main WebSocket listener: TcpListener::bind(&config.bind_addr) in serve() (crates/buzz-relay/src/main.rs:1330). bind_addr comes from the relay config (BUZZ_BIND_ADDR, parsed by parse_bind_addr in config.rs). Any kernel bind failure is wrapped in anyhow with the full address and aborts startup.

Solutions

  1. Check who holds the address: `ss -ltnp 'sport = :<port>'` and stop the duplicate listener.
  2. Change BUZZ_BIND_ADDR to a free port/IP (e.g. 127.0.0.1:3000 for local dev) in .env.
  3. Verify the IP in BUZZ_BIND_ADDR exists: `ip addr` — if not, bind 0.0.0.0:<port> or fix the interface address.
  4. For privileged ports, grant CAP_NET_BIND_SERVICE or front the relay with a proxy that binds 443 for it.
  5. In docker-compose/K8s, make sure only one replica binds a host port when using host networking.

Example fix

# before
BUZZ_BIND_ADDR=10.0.1.5:3000   # IP not present on this host
# error: Failed to bind 10.0.1.5:3000: Cannot assign requested address

# after
BUZZ_BIND_ADDR=0.0.0.0:3000
Defensive patterns

Strategy: validation

Validate before calling

# verify address exists on this host and port is free before launch
ip -4 addr | grep -F "$BIND_IP" || { echo "BUZZ_BIND_ADDR IP not present" >&2; exit 1; }
ss -ltnH "sport = :$BIND_PORT" | grep -q . && { echo "port busy" >&2; exit 1; } || true

Prevention

When it happens

Trigger: Binding the main relay socket fails when: (1) AddrInUse — another relay or any process already listens on that ip:port; (2) PermissionDenied — port < 1024 without privileges; (3) EADDRNOTAVAIL — BUZZ_BIND_ADDR names an IP that does not exist on this host (typical when copying a config from another machine or binding a docker-internal IP directly on the host); (4) EACCES on the port due to local firewall reserved-port ranges (ip_local_reserved_ports / SELinux).

Common situations: Two `just relay` sessions on one dev box; a previous relay still running inside Docker while you start one on the host; BUZZ_BIND_ADDR set to 10.x cluster IP on a laptop; switching a deployment from port 3000 to 443 without granting the capability.

Related errors


AI-assisted analysis of block/buzz@ef2aa1ae38 (2026-08-20). Data as JSON: /api/errors/2e3633783dc422ce. Report an issue: GitHub.

Appendix: source

Thrown at crates/buzz-relay/src/main.rs:1448

        // retains ownership of every delayed close until its 1012 frame has
        // been flushed and acknowledged (or its send loop cancelled).
        let closed = if drain_jitter_ms == 0 {
            drain_conn_manager.drain_all()
        } else {
            drain_conn_manager.drain_all_jittered(drain_jitter_ms).await
        };
        info!(
            connections = closed,
            jitter_ms = drain_jitter_ms,
            max_jitter_ms = MAX_DRAIN_JITTER_MS,
            "Signalled restart close to all live WebSocket connections"
        );
        hard_shutdown_abort
    });

    let tcp_listener = tokio::net::TcpListener::bind(&config.bind_addr)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to bind {}: {e}", config.bind_addr))?;
    info!(addr = %config.bind_addr, "buzz-relay TCP listening");

    #[cfg(unix)]
    if let Some(ref uds_path) = config.uds_path {
        use std::os::unix::fs::FileTypeExt as _;
        match std::fs::symlink_metadata(uds_path) {
            Ok(meta) if meta.file_type().is_socket() => {
                let _ = std::fs::remove_file(uds_path);
            }
            Ok(_) => {
                return Err(anyhow::anyhow!(
                    "BUZZ_UDS_PATH {uds_path} exists but is not a socket"
                ));
            }
            Err(_) => {}
        }
        let uds_listener = tokio::net::UnixListener::bind(uds_path)
            .map_err(|e| anyhow::anyhow!("Failed to bind UDS {uds_path}: {e}"))?;

View on GitHub (pinned to ef2aa1ae38)