block/buzz · error

Failed to bind UDS {uds_path}: {e}

Error message

Failed to bind UDS {uds_path}: {e}

What it means

After the stale-socket cleanup, serve() calls tokio::net::UnixListener::bind(BUZZ_UDS_PATH) and wraps the io::Error. At this point the path is known to be free, so failures come from the environment: parent directory missing (NotFound), no write permission on the directory (PermissionDenied, common when running non-root without a writable /run), the path exceeding the kernel's ~107-byte sun_path limit (InvalidInput on Linux), or a filesystem that does not support unix sockets.

Source

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

        .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 f956e6fe06)

Solutions

  1. Create the parent directory: mkdir -p /run/buzz (entrypoint or Dockerfile) and rerun.
  2. Fix ownership/permissions so the relay user can write there (chown/chmod), or use a writable path like /tmp.
  3. Shorten the path — keep the full absolute path well under ~104 characters.
  4. Move the socket to a local or tmpfs filesystem if the current mount does not support sockets.

Example fix

# before: Error: Failed to bind UDS /run/buzz/relay.sock: No such file or directory (os error 2)

# after: create the dir in the image/entrypoint
RUN mkdir -p /run/buzz && chown relay:relay /run/buzz
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: the parent dir must exist and the path must fit sun_path (~104 bytes on Linux).
fn uds_bindable(path: &str) -> bool {
    let p = std::path::Path::new(path);
    match p.parent() {
        Some(dir) => dir.is_dir() && path.len() < 104,
        None => false,
    }
}

if let Some(uds) = std::env::var("BUZZ_UDS_PATH").ok() {
    assert!(uds_bindable(&uds), "{uds} not bindable — mkdir -p the parent, check perms, shorten the path");
}

Type guard

fn classify_uds_bind_error(e: &std::io::Error) -> &'static str {
    match e.kind() {
        std::io::ErrorKind::NotFound => "parent directory missing",
        std::io::ErrorKind::PermissionDenied => "no write permission on directory",
        std::io::ErrorKind::InvalidInput => "path too long for sun_path",
        _ => "filesystem may not support unix sockets",
    }
}

Try / catch

let uds_listener = match tokio::net::UnixListener::bind(uds_path) {
    Ok(l) => l,
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
        return Err(anyhow!("parent dir missing for {uds_path}: mkdir -p it first"))
    }
    Err(e) => return Err(anyhow!("Failed to bind UDS {uds_path}: {e}")),
};

Prevention

When it happens

Trigger: BUZZ_UDS_PATH=/run/buzz/relay.sock when /run/buzz does not exist; relay user lacking write access to /run or /var/run; deeply nested container paths (long overlayfs/kubelet pod paths) blowing past sun_path; socket placed on an NFS or otherwise socket-hostile mount; readonly-root filesystem with no writable socket dir.

Common situations: Containers running as non-root without tmpfs at /run; missing mkdir -p in entrypoints; distroless images with no pre-created runtime dirs; socket paths under /var/lib/kubelet/pods/... in hostPath mounts.

Related errors


AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16). Data as JSON: /api/errors/06b4ceae38e125b7. Report an issue: GitHub.