block/buzz · critical · anyhow::Error
Failed to bind UDS
Error message
Failed to bind UDS {uds_path}: {e} What it means
After the stale-socket cleanup in serve(), tokio::net::UnixListener::bind(uds_path) creates the actual Unix domain socket. Failure is wrapped with the path and aborts boot. Common causes are filesystem-level: missing or non-writable parent directory, path components denying search/execute permission, a path longer than the kernel's sun_path limit (~104-108 bytes), or a read-only filesystem.
Solutions
- Pre-create the parent directory with writable permissions: `install -d -o <relay-user> -m 0755 /run/buzz` and use BUZZ_UDS_PATH=/run/buzz/relay.sock.
- Shorten BUZZ_UDS_PATH to well under 108 bytes total.
- If SELinux/AppArmor is involved, allow socket creation in that directory context (or move the socket to a permitted location like /run).
- In containers, mount a tmpfs at the socket directory.
- Run the relay as a user that owns the socket directory.
Example fix
# before export BUZZ_UDS_PATH=/var/run/buzz/relay/nested/deep/socket.sock # error: Failed to bind UDS ...: Permission denied (or path too long) # after sudo install -d -o $USER -m 0755 /run/buzz export BUZZ_UDS_PATH=/run/buzz/relay.sock
Defensive patterns
Strategy: validation
Validate before calling
p="$BUZZ_UDS_PATH"
dir=$(dirname "$p")
[ -d "$dir" ] && [ -w "$dir" ] || { echo "socket dir $dir missing or not writable" >&2; exit 1; }
[ "${#p}" -lt 100 ] || { echo "UDS path too long (kernel sun_path limit ~108)" >&2; exit 1; } Prevention
- Keep BUZZ_UDS_PATH short (/run/buzz.sock, not deep nested paths).
- Create and chown the socket directory in deployment (install -d -o <user> /run/buzz) rather than relying on defaults.
- On SELinux hosts, verify the context allows socket creation in the chosen directory.
- In containers, mount a tmpfs at the socket directory so the relay can create sockets.
When it happens
Trigger: UnixListener::bind at crates/buzz-relay/src/main.rs:1349 fails with EACCES (parent dir owned by root, relay running as non-root), ENOENT (parent directory does not exist), ENAMETOOLONG/EINVAL (BUZZ_UDS_PATH longer than the ~108-byte sun_path limit, typical with deeply nested /var/run/very/long/service/name.sock paths), or EROFS (path under a read-only mount).
Common situations: Running the relay as an unprivileged user with BUZZ_UDS_PATH=/var/run/buzz.sock (root-owned /var/run); long namespaced paths under /run/user/1000/... in deeply nested worktrees; SELinux denying socket creation in the target directory; container with a read-only rootfs and no tmpfs at the socket path.
Related errors
- BUZZ_UDS_PATH exists but is not a socket
- git pack cache path must be available
- oauth cache is not a regular file
- Failed to bind
- Failed to bind health port
AI-assisted analysis of block/buzz@ef2aa1ae38 (2026-08-20).
Data as JSON: /api/errors/06b4ceae38e125b7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/buzz-relay/src/main.rs:1466
.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}"))?;
info!(path = %uds_path, "buzz-relay UDS listening");
let router_uds = router.clone();
let mut uds_rx = shutdown_tx.subscribe();
let uds_handle = tokio::spawn(async move {
axum::serve(uds_listener, router_uds.into_make_service())
.with_graceful_shutdown(async move {
uds_rx.changed().await.ok();
})
.await
.ok();
});
let mut tcp_rx = shutdown_tx.subscribe();
axum::serve(
tcp_listener,
router.into_make_service_with_connect_info::<std::net::SocketAddr>(),
)View on GitHub (pinned to ef2aa1ae38)