block/buzz · error · anyhow::Error
failed to sign kind:13534: {e}
Error message
failed to sign kind:13534: {e} What it means
After the member tags are assembled, buzz-admin signs the kind:13534 membership-list event with the relay keypair via EventBuilder::sign_with_keys. Signing fails if the nostr crate cannot serialize the event — a tag that fails standardization, or a custom_created_at outside the accepted u64-seconds range — or if the keypair itself is unusable. Note the timestamp is computed as (existing + 1).max(now); a garbage stored created_at (e.g., milliseconds instead of seconds) overflows on the +1.
Source
Thrown at crates/buzz-admin/src/main.rs:374
};
let members = db.list_relay_members(tenant.community()).await?;
let mut tags: Vec<Tag> = Vec::with_capacity(members.len() + 1);
// NIP-70 protected-event marker — prevents re-broadcasting by third parties.
tags.push(Tag::parse(["-"]).map_err(|e| anyhow::anyhow!("failed to build '-' tag: {e}"))?);
for member in &members {
tags.push(
Tag::parse(["member", &member.pubkey, &member.role])
.map_err(|e| anyhow::anyhow!("failed to build member tag: {e}"))?,
);
}
let event = EventBuilder::new(Kind::Custom(KIND_NIP43_MEMBERSHIP_LIST as u16), "")
.tags(tags)
.custom_created_at(nostr::Timestamp::from(ts))
.sign_with_keys(relay_keypair)
.map_err(|e| anyhow::anyhow!("failed to sign kind:13534: {e}"))?;
let (stored, was_inserted) = db
.replace_addressable_event(tenant.community(), &event, None)
.await?;
if was_inserted {
// Publish to Redis so live clients receive the updated roster.
// Community-global scope (EventTopic::Global) matches the relay's own
// membership-list publish path; the tenant fixes the community.
if let Err(e) = pubsub
.publish_event(tenant, EventTopic::Global, &stored.event)
.await
{
warn!("Redis publish of kind:13534 failed: {e}");
}
}
tracing::info!(
member_count = members.len(),View on GitHub (pinned to dad5a33865)
Solutions
- Read the chained {e} from the message first — it names the exact nostr Error variant (timestamp, key, or tag).
- Check the stored timestamp: SELECT created_at FROM events WHERE kind = 13534; — it must be Unix seconds, not milliseconds; fix the row.
- Confirm BUZZ_RELAY_PRIVATE_KEY is the relay's 32-byte hex or nsec secret (not the public key) with no whitespace or quotes.
- Re-run add-member/remove-member to rebuild and republish the list.
Example fix
-- before: created_at stored in milliseconds SELECT created_at FROM events WHERE kind = 13534; -- 1737000000000 -- after: seconds UPDATE events SET created_at = created_at / 1000 WHERE kind = 13534 AND created_at > 100000000000;
Defensive patterns
Strategy: validation
Validate before calling
-- before republishing kind:13534, confirm the previous created_at is sane Unix seconds SELECT created_at FROM events WHERE kind = 13534; -- valid seconds are < 10^11; anything ~1.7e12 is milliseconds and must be fixed
Try / catch
match run_publish_membership(...).await {
Ok(_) => {},
Err(e) if e.to_string().contains("failed to sign kind:13534") => {
// inspect e.source() chain for the nostr Error variant before retrying;
// do not blind-retry: signing failures are deterministic
log_and_alert(&e);
}
Err(e) => return Err(e),
} Prevention
- Store nostr created_at values in seconds only; enforce with a CHECK (created_at < 100000000000) on event tables.
- Smoke-test the relay key by signing a scratch event before running roster mutations.
- Never run admin mutations with a key you have not already used successfully for signing.
When it happens
Trigger: The previous kind:13534 row's created_at was stored in milliseconds (1.7e12) so existing+1 leaves the valid seconds range; or the BUZZ_RELAY_PRIVATE_KEY parsed into a Keys object that cannot sign (wrong-length material); or one of the member tags serializes to an invalid form.
Common situations: Migrating timestamps between ms and s conventions; switching the relay key without updating the CLI env; a hand-edited membership event row.
Related errors
- sign kind:39000: {e}
- sign kind:39001: {e}
- sign kind:39002: {e}
- failed to build '-' tag: {e}
- failed to build member tag: {e}
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20).
Data as JSON: /api/errors/03915acf95d4b7e0.
Report an issue: GitHub.