block/buzz · error · anyhow::Error

invalid BUZZ_RELAY_PRIVATE_KEY

Error message

invalid BUZZ_RELAY_PRIVATE_KEY: {e}

What it means

Keys::parse accepts only a 32-byte hex secret key or a bech32 nsec… string. Any other shape — truncated or padded hex, '0x' prefix, an npub (public key) pasted by mistake, surrounding quotes/whitespace from a .env file, or a passphrase — returns Err, surfaced as 'invalid BUZZ_RELAY_PRIVATE_KEY'. The check runs after the presence check, so the variable is set but its value is not a usable secret.

Solutions

  1. Hex form must be exactly 64 chars: echo -n "$BUZZ_RELAY_PRIVATE_KEY" | wc -c (expect 64).
  2. If using nsec form, re-copy it fully — bech32 checksums fail loudly on any typo.
  3. Make sure it is the SECRET key (hex or nsec), not the npub/public key.
  4. Strip quotes/whitespace: export BUZZ_RELAY_PRIVATE_KEY=$(echo "$BUZZ_RELAY_PRIVATE_KEY" | tr -d '"' | tr -d "'").

Example fix

# before
BUZZ_RELAY_PRIVATE_KEY="8a3f..."   # quotes stored as part of the value by a deploy tool
# after
BUZZ_RELAY_PRIVATE_KEY=8a3f...       # bare 64-hex value, no quotes
Defensive patterns

Strategy: validation

Validate before calling

# accept only 64-hex or nsec… before running the command
key="$BUZZ_RELAY_PRIVATE_KEY"
case "$key" in
  nsec1*) ;;
  *) [ "${#key}" -eq 64 ] && echo "$key" | grep -qE '^[0-9a-fA-F]{64}$' || { echo 'bad key format' >&2; exit 3; } ;;
esac

Type guard

fn is_usable_secret(key: &str) -> bool {
    let k = key.trim();
    (k.len() == 64 && k.chars().all(|c| c.is_ascii_hexdigit()))
        || k.starts_with("nsec1")
}

Prevention

When it happens

Trigger: BUZZ_RELAY_PRIVATE_KEY contains an npub/public key, a 63- or 65-char string, a quoted value from .env, or an nsec with a typo (bech32 checksum failure).

Common situations: Copy-paste truncation; using the public key because it was the handier string from the relay logs; .env values wrapped in quotes by a deployment tool.

Related errors


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

Appendix: source

Thrown at crates/buzz-admin/src/main.rs:557

        "NIP-43 membership list published by buzz-admin"
    );
    Ok(())
}

/// Connect to DB, Redis pub/sub, and load the relay keypair.
///
/// `BUZZ_RELAY_PRIVATE_KEY` is required — the CLI signs kind:13534 events.
async fn connect_member_services() -> Result<(Db, Arc<PubSubManager>, Keys)> {
    let db = connect_db().await?;

    let relay_keypair = {
        let hex = std::env::var("BUZZ_RELAY_PRIVATE_KEY").map_err(|_| {
            anyhow::anyhow!(
                "BUZZ_RELAY_PRIVATE_KEY is required for add-member/remove-member.\n\
                 The relay must have a stable signing key to publish kind:13534 events."
            )
        })?;
        Keys::parse(&hex).map_err(|e| anyhow::anyhow!("invalid BUZZ_RELAY_PRIVATE_KEY: {e}"))?
    };

    let redis_url =
        std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string());

    let redis_pool = {
        let cfg = deadpool_redis::Config::from_url(&redis_url);
        cfg.create_pool(Some(deadpool_redis::Runtime::Tokio1))
            .map_err(|e| anyhow::anyhow!("Redis pool creation failed: {e}"))?
    };

    let pubsub = Arc::new(
        PubSubManager::new(&redis_url, redis_pool)
            .await
            .map_err(|e| anyhow::anyhow!("PubSub init failed: {e}"))?,
    );

    Ok((db, pubsub, relay_keypair))

View on GitHub (pinned to ef2aa1ae38)