block/buzz · critical
failed to sign moderation notice: {e}
Error message
failed to sign moderation notice: {e} What it means
After constructing the moderation notice event, the relay signs it with the relay's own keypair via `sign_with_keys`. This error wraps any failure from that signing operation (map_err into anyhow), meaning the notice event could not be signed and therefore was not stored or delivered.
Source
Thrown at crates/buzz-relay/src/handlers/moderation_notices.rs:175
// from `idempotency_ts` (the outbox row's immutable `created_at`). Two
// workers racing on the same outbox row produce byte-identical Nostr events
// (same pubkey + created_at + kind + tags + content = same SHA256 event ID).
// `insert_event`'s ON CONFLICT DO NOTHING ensures exactly one row is
// durably persisted regardless of how many workers reach this point.
let source_id = notice.source_id();
let tags = vec![
Tag::parse(["h", &dm_channel_id.to_string()])?,
Tag::parse([MODERATION_SOURCE_TAG, &source_id.to_string()])?,
];
let ts = nostr::Timestamp::from(idempotency_ts.timestamp() as u64);
let event = EventBuilder::new(
Kind::Custom(KIND_STREAM_MESSAGE as u16),
notice.body(tenant),
)
.tags(tags)
.custom_created_at(ts)
.sign_with_keys(&state.relay_keypair)
.map_err(|e| anyhow::anyhow!("failed to sign moderation notice: {e}"))?;
let (stored, _inserted) = state
.db
.insert_event(tenant.community(), &event, Some(dm_channel_id))
.await?;
let kind_u32 = event_kind_u32(&stored.event);
dispatch_persistent_event(tenant, state, &stored, kind_u32, &relay_pubkey_hex, None).await;
Ok(())
}
/// Publish the relay-signed kind:0 "{host} Moderation" profile so clients can
/// render the DM author with a recognizable name. Replaceable (NIP-01), so
/// re-emitting is idempotent — the latest wins.
async fn publish_moderation_profile(
tenant: &TenantContext,
state: &Arc<AppState>,View on GitHub (pinned to eed74bde2f)
Solutions
- Check the relay keypair configuration (.env / key file) and confirm the secret key is a valid 32-byte secp256k1 scalar.
- Inspect the wrapped inner error message (`failed to sign moderation notice: {e}`) — it names the underlying secp256k1 cause.
- Regenerate or restore the relay identity key from a valid source and restart the relay.
- Add a startup check that loads and test-signs with the relay keypair so misconfiguration fails fast.
Example fix
// before — keypair loaded unchecked
let keypair = relay_keypair_from_env()?;
// after — fail fast at startup
let keypair = relay_keypair_from_env()?;
let probe = EventBuilder::new(Kind::Custom(0), "health-check");
probe.sign_with_keys(&keypair).context("relay keypair is unusable; fix BUZZ_RELAY_KEY")?; Defensive patterns
Strategy: try-catch
Try / catch
match send_moderation_notice(&state, recipient, notice, ts).await {
Err(e) if e.to_string().contains("failed to sign moderation notice") => {
tracing::error!(cause = %e, "relay keypair unusable — halting notice delivery");
// page an operator / fail startup rather than retrying blindly
}
Err(e) => return Err(e),
Ok(v) => Ok(v),
} Prevention
- Load and test-sign with the relay keypair at startup so bad key config fails fast.
- Keep the relay secret key in a validated secret store, not a hand-edited env file.
- Alert on any signing failure — it indicates identity corruption, not a transient fault.
- Back up the relay identity key and verify it after restores or migrations.
When it happens
Trigger: Calling `send_moderation_notice` when `state.relay_keypair` cannot produce a valid signature — corrupted or zeroed relay secret key, secp256k1 signing failure on the constructed event, or a keypair whose public key does not match the event's pubkey field.
Common situations: Relay identity key misconfigured (e.g. empty or invalid secret in env/config); key file corrupted or regenerated between restarts; cryptographic library failing on malformed key material at runtime; keypair loaded from a migration that produced a zero key.
Related errors
- failed to sign system message: {e}
- moderation notice recipient must be a 32-byte pubkey, got {}
- failed to sign member snapshot: {error}
- sign roster
- ban requires target_pubkey
AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30).
Data as JSON: /api/errors/01ccfd0f0deee447.
Report an issue: GitHub.