block/buzz · error · anyhow::Error

setup-mode relay connect error

Error message

setup-mode relay connect error: {e}

What it means

Setup mode's first relay interaction is HarnessRelay::connect — establish the WebSocket to BUZZ_RELAY_URL and complete the NIP-42 auth handshake with the harness key. This wrapped error means that connect failed, so setup mode exits before any subscription or watermark is attempted.

Solutions

  1. Confirm the relay is running and the URL scheme is explicit: ws://localhost:3000 or wss://host
  2. Check the harness env: BUZZ_RELAY_URL, BUZZ_PRIVATE_KEY, relay auth tag
  3. Test reachability independently (curl the relay's HTTP NIP-11 endpoint on the same host)
  4. Retry after the relay is healthy

Example fix

# before
export BUZZ_RELAY_URL=localhost:3000
# after
export BUZZ_RELAY_URL=ws://localhost:3000
Defensive patterns

Strategy: retry

Validate before calling

#!/usr/bin/env bash
: "${BUZZ_RELAY_URL:?BUZZ_RELAY_URL required for setup mode}"
case "$BUZZ_RELAY_URL" in ws://*|wss://*) ;; *) echo "BUZZ_RELAY_URL must start with ws:// or wss://" >&2; exit 1;; esac
nc -z "$(echo "$BUZZ_RELAY_URL" | sed -E 's|^wss?://||; s|:[0-9]+$||')" "$(echo "$BUZZ_RELAY_URL" | sed -nE 's|.*:([0-9]+)$|\1|p')" \
  || { echo 'relay port not reachable' >&2; exit 1; }

Try / catch

// Retry transient connect failures; fail fast on auth-shaped ones
loop {
    match HarnessRelay::connect(&url, &keys, &pubkey, auth_tag).await {
        Err(e) if is_auth_error(&e) => return Err(e),          // do not retry bad credentials
        Err(_) if attempts < MAX => { backoff.wait().await; }
        other => return other.map(|_| ()),
    }
}

Prevention

When it happens

Trigger: BUZZ_RELAY_URL wrong (typo, missing ws:// or wss:// scheme), relay not running or unreachable, TLS failure on wss://, or the auth handshake rejected due to an invalid/expired auth tag.

Common situations: Local relay not started (just relay missing from the workflow); staging URL changed; auth tag env rotated while the harness kept the old value; firewall/proxy blocking the WebSocket upgrade.

Related errors


AI-assisted analysis of block/buzz@6c35e82bd5 (2026-08-20). Data as JSON: /api/errors/df1db88f87f3a38d. Report an issue: GitHub.

Appendix: source

Thrown at crates/buzz-acp/src/setup_mode.rs:344

    );

    let pubkey_hex = config.keys.public_key().to_hex();

    // Parse BUZZ_AUTH_TAG for relay membership / NIP-OA.
    let relay_auth_tag: Option<nostr::Tag> = std::env::var("BUZZ_AUTH_TAG")
        .ok()
        .filter(|s| !s.is_empty())
        .and_then(|s| buzz_sdk::nip_oa::parse_auth_tag(&s).ok());

    let startup_watermark: u64 = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();

    let mut relay =
        HarnessRelay::connect(&config.relay_url, &config.keys, &pubkey_hex, relay_auth_tag)
            .await
            .map_err(|e| anyhow::anyhow!("setup-mode relay connect error: {e}"))?;

    if let Err(e) = relay.set_startup_watermark(startup_watermark).await {
        tracing::warn!("setup-mode: failed to set startup watermark: {e}");
    }

    relay
        .subscribe_membership_notifications()
        .await
        .map_err(|e| anyhow::anyhow!("setup-mode membership subscribe error: {e}"))?;

    tracing::info!("setup-mode: connected and subscribed to membership notifications");

    let rest_client = relay.rest_client();
    let mut author_gate_ctx =
        crate::InboundAuthorGate::connect(&rest_client, &pubkey_hex, "setup startup").await;

    // Resolve owner for author-gate (same priority as normal mode).
    let startup_owner = crate::resolve_agent_owner(&config);

View on GitHub (pinned to 6c35e82bd5)