block/buzz · error
mesh endpoint bind on {} failed: {e}
Error message
mesh endpoint bind on {} failed: {e} What it means
boot_mesh (mesh_boot.rs) runs only when BUZZ_MESH is on/true/1 (strict opt-in, config.rs:605). It binds an iroh QUIC endpoint — MeshEndpoint::bind (endpoint.rs:19) builds Endpoint with the given bind_addr and RelayMode::Disabled, and both builder and bind errors collapse into MeshError::Transport(String) — on BUZZ_MESH_BIND_ADDR, default 0.0.0.0:3478 (UDP). This error wraps that failure with the address. Failure is fatal by design: an operator who asked for the mesh gets it or gets told why not.
Source
Thrown at crates/buzz-relay/src/mesh_boot.rs:426
/// sets `BUZZ_MESH=on` wants the mesh or wants to know why not; silently
/// booting meshless would be the same class of bug as silently dropping to a
/// default tenant.
pub async fn boot_mesh(
config: &Config,
redis_pool: deadpool_redis::Pool,
db: buzz_db::Db,
relay_keypair: &nostr::Keys,
shutting_down: Arc<AtomicBool>,
) -> anyhow::Result<Option<MeshHandle>> {
if !config.mesh.enabled {
tracing::info!("mesh disabled (BUZZ_MESH is not 'on') — single-instance behavior");
return Ok(None);
}
let endpoint = MeshEndpoint::bind(config.mesh.bind_addr)
.await
.map_err(|e| {
anyhow::anyhow!(
"mesh endpoint bind on {} failed: {e}",
config.mesh.bind_addr
)
})?;
let runtime_id = endpoint.runtime_id();
let addrs = advertise_addrs(&endpoint);
tracing::info!(
runtime_id = %runtime_id,
bind_addr = %config.mesh.bind_addr,
advertise_addrs = ?addrs,
"mesh endpoint bound"
);
let mut local_record = GossipRecord::new(runtime_id, addrs.clone(), PROTO_VERSION);
local_record.capabilities = capabilities();
// Anchor ready-record acceptance to this deployment's relay identity: all
// pods share the relay signing key, so a seed attested by any other key is
// foreign and rejected (Wren's review — possession is not authorization).View on GitHub (pinned to f956e6fe06)
Solutions
- Find who holds the UDP port: ss -lunp 'sport = :3478' — stop that process or move the mesh elsewhere.
- Set BUZZ_MESH_BIND_ADDR to a free UDP port and open it in firewalls/security groups so peers can dial it.
- If the mesh was not intended, unset BUZZ_MESH (anything other than on/true/1 keeps exact single-instance behavior).
- In containers, ensure UDP is allowed by securityContext/NetworkPolicy and the bind IP exists on an interface.
Example fix
# before: BUZZ_MESH=on with the default bind — coturn already owns UDP 3478 # Error: mesh endpoint bind on 0.0.0.0:3478 failed: ... # after BUZZ_MESH=on BUZZ_MESH_BIND_ADDR=0.0.0.0:3479
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm the mesh UDP port is free before booting with BUZZ_MESH=on.
fn udp_port_available(addr: std::net::SocketAddr) -> bool {
std::net::UdpSocket::bind(addr).is_ok()
}
if let Ok(mesh) = std::env::var("BUZZ_MESH") {
if mesh.eq_ignore_ascii_case("on") || mesh == "true" || mesh == "1" {
let bind: std::net::SocketAddr = std::env::var("BUZZ_MESH_BIND_ADDR")
.unwrap_or_else(|_| "0.0.0.0:3478".into())
.parse()
.expect("BUZZ_MESH_BIND_ADDR must parse");
assert!(udp_port_available(bind), "mesh UDP {bind} busy — often coturn/STUN on 3478");
}
} Type guard
fn is_mesh_bind_error(e: &anyhow::Error) -> bool {
e.to_string().starts_with("mesh endpoint bind")
} Try / catch
let endpoint = match MeshEndpoint::bind(config.mesh.bind_addr).await {
Ok(ep) => ep,
Err(e) => {
tracing::error!(addr = %config.mesh.bind_addr, %e,
"mesh bind failed — check `ss -lunp 'sport = :3478'` for a STUN/TURN holder");
return Err(anyhow!("mesh endpoint bind on {} failed: {e}", config.mesh.bind_addr));
}
}; Prevention
- Never co-locate the mesh and a TURN/STUN server on UDP 3478 — pick distinct ports in env templates
- Add a UDP-port pre-flight to entrypoints when BUZZ_MESH=on
- Document BUZZ_MESH_BIND_ADDR next to firewall/security-group rules so they change together
When it happens
Trigger: BUZZ_MESH=on while UDP 3478 is already taken — 3478 is the standard STUN port, so coturn/turnserver or another relay replica commonly owns it; container seccomp/firewall denying UDP socket creation; BUZZ_MESH_BIND_ADDR naming an IP that does not exist on the host.
Common situations: Co-locating the relay with a TURN server (both default to 3478); k8s hostNetwork pods; multiple mesh-enabled replicas on one node without distinct BUZZ_MESH_BIND_ADDR; restrictive container securityContext blocking UDP.
Related errors
- Failed to bind health port {}: {e}
- Failed to bind {}: {e}
- mesh ready-registry publish failed: {e}
- git conformance probe failed: {e}
- BUZZ_UDS_PATH {uds_path} exists but is not a socket
AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16).
Data as JSON: /api/errors/bc2bee53f111cbf9.
Report an issue: GitHub.