xai-org/grok-build · error

bind {}: {e}

Error message

bind {}: {e}

What it means

serve() failed to bind the diagnostics Unix domain socket at the given path. Any existing socket file is removed first, so this error comes from the OS refusing the bind (unwritable/nonexistent parent directory, path too long, or a race where another process re-created the socket). The diag server never starts.

Source

Thrown at crates/codegen/xai-grok-diag-server/src/lib.rs:412

/// Bind the listener and spawn the server task.
/// Binding happens before this returns, so a bind failure surfaces synchronously.
/// `log_file` is the daemon log served by `/logs` (`None` means `/logs` is 404).
pub async fn serve(
    listener: DiagListener,
    handle: DiagHandle,
    log_file: Option<PathBuf>,
) -> anyhow::Result<BoundDiag> {
    let ctx = DiagContext {
        handle,
        log_file: log_file.map(Arc::new),
    };
    match listener {
        #[cfg(unix)]
        DiagListener::Unix(path) => {
            let _ = fs::remove_file(&path);
            let listener =
                UnixListener::bind(&path).map_err(|e| anyhow!("bind {}: {e}", path.display()))?;
            use std::os::unix::fs::PermissionsExt as _;
            if let Err(e) = fs::set_permissions(&path, fs::Permissions::from_mode(0o600)) {
                tracing::warn!(
                    path = %path.display(),
                    error = %e,
                    "failed to restrict diagnostics socket permissions"
                );
            }
            let task = tokio::spawn(async move {
                if let Err(e) = axum::serve(listener, router(ctx)).await {
                    tracing::warn!(error = %e, "diagnostics server exited");
                }
            });
            Ok(BoundDiag {
                addr: format!("unix:{}", path.display()),
                port: None,
                task,
            })

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Verify the socket's parent directory exists and is writable by the current user; create it if needed
  2. Shorten the socket path (e.g. use a shorter tmp dir) if it exceeds the Unix socket path limit
  3. Check no other live process owns the socket path (ss -x | grep <name>) and stop it
  4. Run in an environment (container/sandbox) that permits creating Unix sockets in the chosen directory

Example fix

// before
let path = "/very/long/cache/path/with/many/components/grok-diag.sock";
// after
let path = std::env::temp_dir().join("grok-diag.sock"); // short, guaranteed-writable path
Defensive patterns

Strategy: validation

Validate before calling

let dir = path.parent().expect("socket path has parent");
if !dir.exists() {
    std::fs::create_dir_all(dir)?;
}
if path.as_os_str().len() > 100 {
    return Err(anyhow!("socket path too long: {}", path.display()));
}

Type guard

fn socket_path_ok(path: &std::path::Path) -> bool {
    path.parent().map(|d| d.is_dir()).unwrap_or(false)
        && path.as_os_str().len() <= 100
}

Try / catch

match serve(ctx, DiagListener::Unix(path)).await {
    Ok(h) => h,
    Err(e) if e.to_string().starts_with("bind ") => {
        eprintln!("diag unix socket bind failed: {e:#}; check dir permissions/path length");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: DiagListener::Unix(path) where the parent directory does not exist or is not writable, the socket path exceeds the ~104-char Unix socket limit, or another process concurrently holds the path as a live socket.

Common situations: XDG_RUNTIME_DIR or temp dir cleaned up/never created; running under a sandbox/container without write access to the socket directory; very long cache paths exceeding sockaddr_un limits.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/a815e32f5584241a. Report an issue: GitHub.