block/buzz · critical · anyhow::Error
mesh endpoint bind on
Error message
mesh endpoint bind on {} failed: {e} What it means
When BUZZ_MESH=on, mesh_boot binds an iroh QUIC/UDP endpoint (MeshEndpoint::bind → iroh Endpoint::builder().bind_addr(addr).bind()) on config.mesh.bind_addr (BUZZ_MESH_BIND_ADDR, see config.rs:608). A bind failure is wrapped with the address and mesh boot aborts, which fails relay startup — the relay refuses to run a half-meshed instance.
Solutions
- Check the UDP listener: `ss -lunp 'sport = :<port>'` and stop the duplicate process/pod.
- Set BUZZ_MESH_BIND_ADDR to 0.0.0.0:<port> (or a free port) and restart.
- Verify the IP in BUZZ_MESH_BIND_ADDR exists on the host (`ip addr`).
- If mesh is not needed for this deployment, set BUZZ_MESH to something other than 'on' — mesh_boot then logs 'mesh disabled' and returns Ok(None).
Example fix
# before BUZZ_MESH=on BUZZ_MESH_BIND_ADDR=10.2.3.4:7777 # IP absent on this node # after BUZZ_MESH=on BUZZ_MESH_BIND_ADDR=0.0.0.0:7777
Defensive patterns
Strategy: validation
Validate before calling
# UDP ports need -u; verify mesh bind is free before enabling mesh
if ss -lunH "sport = :${BUZZ_MESH_BIND_ADDR##*:}" | grep -q .; then
echo "mesh UDP port busy" >&2; exit 1
fi
[ "${BUZZ_MESH:-off}" = "on" ] || echo "mesh off — bind not required" Prevention
- Remember mesh binds UDP, not TCP — check with `ss -lun`, regular TCP checks will miss conflicts.
- Use port 0 or 0.0.0.0:0 for dev mesh instances to let the OS assign a free port when discovery port does not matter.
- Give each replica its own BUZZ_MESH_BIND_ADDR in host-networked deployments.
- Leave BUZZ_MESH off for single-instance local dev — mesh_boot then skips binding entirely.
When it happens
Trigger: The iroh endpoint bind at crates/buzz-relay/src/mesh_boot.rs:423 fails when: (1) the UDP port in BUZZ_MESH_BIND_ADDR is already bound by another relay/mesh instance (AddrInUse — note UDP, so `ss -ltn` will not show it); (2) the address family/IP does not exist on the host (EADDRNOTAVAIL); (3) privileged UDP port without CAP_NET_BIND_SERVICE; (4) firewall/SELinux denying UDP bind.
Common situations: Two relay replicas configured with the same BUZZ_MESH_BIND_ADDR on one host (staging ArgoCD rollout where old pod overlaps with new); BUZZ_MESH_BIND_ADDR copied from the Terraform stack with a VPC IP that does not exist on the node; local dev with a leftover relay process still holding the UDP socket.
Related errors
- Failed to bind
- Failed to bind health port
- mesh ready-registry publish failed
- BUZZ_UDS_PATH exists but is not a socket
- channel discovery error
AI-assisted analysis of block/buzz@934f3325c3 (2026-08-20).
Data as JSON: /api/errors/bc2bee53f111cbf9.
Report an issue: GitHub.
Appendix: 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 934f3325c3)