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

  1. Check the chained {e} for the nostr Error variant.
  2. 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).
  3. Confirm relay_keys came from --relay-key/BUZZ_RELAY_PRIVATE_KEY (the intended stable key), not the ephemeral warning path.
  4. 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

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


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)