block/buzz · error · anyhow::Error
sign kind:39001
Error message
sign kind:39001: {e} What it means
reconcile-channels builds the kind:39001 (KIND_NIP29_GROUP_ADMINS) addressable event — a d tag with the channel UUID plus a p tag (pubkey, role) for every member whose role is owner or admin — then signs it with relay_keys. A failure at sign_with_keys is a nostr signing/serialization failure, not a tag-parse failure (those propagate directly from Tag::parse with the tag error).
Solutions
- Check the chained {e} for the nostr Error variant.
- Validate admin/owner pubkey columns: SELECT pubkey FROM relay_members WHERE role IN ('owner','admin'); — raw-byte length must be 32 (hex-encodes to 64).
- Confirm relay_keys came from --relay-key/BUZZ_RELAY_PRIVATE_KEY (the intended stable key), not the ephemeral warning path.
- Fix rows or key, then re-run reconcile-channels.
Example fix
-- before: pubkey stored as 33 raw bytes (parity byte kept) SELECT octet_length(pubkey) FROM relay_members WHERE role = 'admin'; -- 33 -- after: strip to the 32-byte x-only form UPDATE relay_members SET pubkey = substring(pubkey FROM 2 FOR 32) WHERE octet_length(pubkey) = 33;
Defensive patterns
Strategy: validation
Validate before calling
-- precheck the pubkeys that feed kind:39001's p tags
SELECT pubkey, octet_length(pubkey) AS bytes, role
FROM relay_members WHERE role IN ('owner', 'admin');
-- every row must show bytes = 32 (hex-encodes to 64 chars); fix any outlier first Prevention
- Constrain the column: CHECK (octet_length(pubkey) = 32) on raw-byte pubkey columns (or CHAR(64) hex form).
- Audit role values on member insert; only owner/admin rows enter this event.
- Run reconcile with a verified key so signing failures localize to data, not key material.
When it happens
Trigger: Signing with key material that cannot produce a signature; a p-tag pubkey that hex-encodes to a value nostr rejects during serialization; relay_keys generated as ephemeral in a previous broken run.
Common situations: Member pubkeys stored as raw bytes with invalid length in the DB (hex::encode of a 33-byte value yields 66 chars, which parse rejects or sign chokes on); rotating the relay key between runs.
Related errors
- failed to sign kind:13534
- sign kind:39000
- sign kind:39002
- failed to build member tag
- failed to build '-' tag
AI-assisted analysis of block/buzz@ef2aa1ae38 (2026-08-20).
Data as JSON: /api/errors/ffadcc64d869798b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/buzz-admin/src/main.rs:749
.map_err(|e| anyhow::anyhow!("sign kind:39000: {e}"))?;
db.replace_addressable_event(tenant.community(), &event, Some(channel.id))
.await?;
}
// kind:39001 — admins
{
let mut tags: Vec<Tag> = vec![Tag::parse(["d", &channel_id_str])?];
for m in members
.iter()
.filter(|m| m.role == "owner" || m.role == "admin")
{
let pk = hex::encode(&m.pubkey);
tags.push(Tag::parse(["p", &pk, &m.role])?);
}
let event = EventBuilder::new(Kind::Custom(KIND_NIP29_GROUP_ADMINS as u16), "")
.tags(tags)
.sign_with_keys(&relay_keys)
.map_err(|e| anyhow::anyhow!("sign kind:39001: {e}"))?;
db.replace_addressable_event(tenant.community(), &event, Some(channel.id))
.await?;
}
}
// kind:39002 — members
{
let mut tags: Vec<Tag> = vec![Tag::parse(["d", &channel_id_str])?];
for m in &members {
let pk = hex::encode(&m.pubkey);
tags.push(Tag::parse(["p", &pk, "", &m.role])?);
}
let event = EventBuilder::new(Kind::Custom(39002), "")
.tags(tags)
.sign_with_keys(&relay_keys)
.map_err(|e| anyhow::anyhow!("sign kind:39002: {e}"))?;
db.replace_addressable_event(tenant.community(), &event, Some(channel.id))
.await?;View on GitHub (pinned to ef2aa1ae38)