{"record":{"id":"cc72b385f54a4938","repo":"block/buzz","slug":"cannot-derive-a-community-host-from-buzz-relay-url","errorCode":null,"errorMessage":"Cannot derive a community host from BUZZ_RELAY_URL ({:?}); a resolvable host is required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true","messagePattern":"Cannot derive a community host from BUZZ_RELAY_URL \\((.+?)\\); a resolvable host is required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/buzz-relay/src/main.rs","lineNumber":354,"sourceCode":"        ));\n    }\n\n    // NIP-43 / multi-tenant: seed the deployment's *own* community before any\n    // membership backfill or owner bootstrap, so those writes are scoped to a\n    // real `(community_id, pubkey)` and not a global pubkey. The host is derived\n    // from `relay_url` with the *same* normalization request resolution uses\n    // (`relay_url_authority` → `normalize_host`), so the bootstrapped owner lands\n    // in exactly the community that live requests for this host will resolve to.\n    //\n    // `ensure_configured_community` is idempotent, so this is safe to run every\n    // startup. An empty authority (unparseable `relay_url`)\n    // is a misconfiguration — fail fast when membership is enforced rather than\n    // seeding an empty-host community that no request can ever resolve to.\n    let deployment_community = {\n        let host = buzz_relay::tenant::relay_url_authority(&config.relay_url);\n        if host.is_empty() {\n            if config.require_relay_membership {\n                return Err(anyhow::anyhow!(\n                    \"Cannot derive a community host from BUZZ_RELAY_URL ({:?}); a resolvable host is required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true\",\n                    config.relay_url\n                ));\n            }\n            error!(\n                relay_url = %config.relay_url,\n                \"Could not derive a community host from relay_url; skipping membership backfill/bootstrap (non-fatal, membership not required)\"\n            );\n            None\n        } else {\n            match db.ensure_configured_community_for_bootstrap(&host).await {\n                Ok(record) => {\n                    info!(host = %record.host, community = %record.id, \"Deployment community ensured\");\n                    Some(record.id)\n                }\n                Err(e) => {\n                    if config.require_relay_membership {\n                        error!(\"Fatal: failed to ensure deployment community with membership enforcement enabled: {e}\");","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/block/buzz/blob/ef2aa1ae38fadcc0bc22b8bf6ed96b35933146be/crates/buzz-relay/src/main.rs#L336-L372","documentation":"Fatal startup error: the relay derives its deployment community host from BUZZ_RELAY_URL via relay_url_authority(), and that returned an empty authority — the URL has no parseable host. With BUZZ_REQUIRE_RELAY_MEMBERSHIP=true, bootstrapping an empty-host community no request could ever resolve to is a misconfiguration, so startup fails fast.","triggerScenarios":"BUZZ_RELAY_URL is empty, missing a scheme (\"relay.example.com\" instead of \"wss://relay.example.com\"), or otherwise unparseable, while BUZZ_REQUIRE_RELAY_MEMBERSHIP=true. Without enforcement the same condition only logs a warning and skips membership bootstrap.","commonSituations":"Setting just a host:port (\"localhost:3000\") without ws://; a templating bug injecting an empty variable into the URL; the var being consumed by a proxy config instead of the relay.","solutions":["Set BUZZ_RELAY_URL to a full URL with scheme and host: wss://relay.example.com (prod) or ws://localhost:3000 (dev)","Ensure the host matches how clients will address the relay — the bootstrapped owner community is keyed to it","Restart; if you truly did not want membership enforcement, BUZZ_REQUIRE_RELAY_MEMBERSHIP=false downgrades this to a warning"],"exampleFix":"# before\nexport BUZZ_RELAY_URL=localhost:3000\n\n# after\nexport BUZZ_RELAY_URL=ws://localhost:3000","handlingStrategy":"validation","validationCode":"# Deploy-time guard:\npython3 - <<'EOF'\nfrom urllib.parse import urlparse\nimport os\nu = os.environ.get(\"BUZZ_RELAY_URL\", \"\")\nassert urlparse(u).scheme in (\"ws\", \"wss\") and urlparse(u).hostname, \\\n    f\"BUZZ_RELAY_URL must be a full ws:// or wss:// URL, got: {u!r}\"\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always configure BUZZ_RELAY_URL with an explicit ws:// or wss:// scheme","The URL host defines the deployment community — keep it stable and matching how clients connect"],"tags":["config","url","nip-43","startup","buzz-relay"],"backgroundTag":"invalid-url","analyzedSha":"ef2aa1ae38fadcc0bc22b8bf6ed96b35933146be","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}