block/buzz · critical · anyhow::Error

Failed to bootstrap relay owner (required when…

Error message

Failed to bootstrap relay owner (required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true): {e}

What it means

Fatal startup error: db.bootstrap_owner(community, owner_pubkey) — the idempotent call guaranteeing RELAY_OWNER_PUBKEY holds the owner role in the deployment community — failed while BUZZ_REQUIRE_RELAY_MEMBERSHIP=true. A membership-enforcing relay without a bootstrapped owner cannot be administered, so the relay fails fast rather than start broken.

Solutions

  1. Read the {e} detail to classify connectivity vs permissions vs schema
  2. Verify the DB role can write the membership/role tables used by bootstrap_owner
  3. Restart after Postgres is stable — the call is idempotent and re-runs safely every startup
  4. Confirm RELAY_OWNER_PUBKEY is the intended admin key before retrying, since it will be promoted to owner
Defensive patterns

Strategy: retry

Validate before calling

# Verify the role can write membership tables before deploy:
psql "$DATABASE_URL" -c "insert into relay_members (community_id, pubkey, role) values ('00000000-0000-0000-0000-000000000000','00','member') on conflict do nothing" 2>&1 | head -1

Try / catch

// Distinguish transient (restart with backoff) from permanent (grants/schema)
// by the wrapped sqlx error text before alerting.

Prevention

When it happens

Trigger: DB failure during the owner upsert/role promotion: transient connection error, permission denied on the members/roles tables, or a schema/constraint mismatch from unapplied migrations.

Common situations: Same class as the other boot-time membership writes: Postgres not fully healthy at relay boot, least-privilege role missing rights, or a database restored without the full schema.

Related errors


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

Appendix: source

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

                }
            }
        }
    }

    // NIP-43: ensure the configured relay owner always holds the owner role
    // within the deployment community.
    if let (Some(community), Some(owner_pubkey)) =
        (deployment_community, config.relay_owner_pubkey.as_ref())
    {
        match db.bootstrap_owner(community, owner_pubkey).await {
            Ok(()) => info!(pubkey = %owner_pubkey, "Relay owner bootstrapped"),
            Err(e) => {
                if config.require_relay_membership {
                    // Membership enforcement is on — a missing owner means no one
                    // can administer the relay. Fail fast rather than silently start
                    // in a broken state.
                    error!("Fatal: failed to bootstrap relay owner with membership enforcement enabled: {e}");
                    return Err(anyhow::anyhow!(
                        "Failed to bootstrap relay owner (required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true): {e}"
                    ));
                } else {
                    error!(
                        "Failed to bootstrap relay owner (non-fatal, membership not required): {e}"
                    );
                }
            }
        }
    }

    // NIP-33: backfill d_tag for any existing parameterized replaceable events
    // that predate the column addition. Idempotent — no-ops when fully populated.
    match db.backfill_d_tags().await {
        Ok(0) => {}
        Ok(n) => info!("Backfilled d_tag for {n} NIP-33 events"),
        Err(e) => error!("Failed to backfill d_tags: {e}"),
    }

View on GitHub (pinned to ef2aa1ae38)