{"record":{"id":"a75e4627e8ae756b","repo":"block/buzz","slug":"sign-kind-39000-e","errorCode":null,"errorMessage":"sign kind:39000: {e}","messagePattern":"sign kind:39000: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/buzz-admin/src/main.rs","lineNumber":731,"sourceCode":"                    if !desc.is_empty() {\n                        tags.push(Tag::parse([\"about\", desc])?);\n                    }\n                }\n                if channel.visibility == \"private\" {\n                    tags.push(Tag::parse([\"private\"])?);\n                } else {\n                    tags.push(Tag::parse([\"public\"])?);\n                }\n                if channel.channel_type == \"dm\" {\n                    tags.push(Tag::parse([\"hidden\"])?);\n                }\n                tags.push(Tag::parse([\"closed\"])?);\n                tags.push(Tag::parse([\"t\", &channel.channel_type])?);\n\n                let event = EventBuilder::new(Kind::Custom(39000), \"\")\n                    .tags(tags)\n                    .sign_with_keys(&relay_keys)\n                    .map_err(|e| anyhow::anyhow!(\"sign kind:39000: {e}\"))?;\n                db.replace_addressable_event(tenant.community(), &event, Some(channel.id))\n                    .await?;\n            }\n\n            // kind:39001 — admins\n            {\n                let mut tags: Vec<Tag> = vec![Tag::parse([\"d\", &channel_id_str])?];\n                for m in members\n                    .iter()\n                    .filter(|m| m.role == \"owner\" || m.role == \"admin\")\n                {\n                    let pk = hex::encode(&m.pubkey);\n                    tags.push(Tag::parse([\"p\", &pk, &m.role])?);\n                }\n                let event = EventBuilder::new(Kind::Custom(KIND_NIP29_GROUP_ADMINS as u16), \"\")\n                    .tags(tags)\n                    .sign_with_keys(&relay_keys)\n                    .map_err(|e| anyhow::anyhow!(\"sign kind:39001: {e}\"))?;","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/block/buzz/blob/ef2aa1ae38fadcc0bc22b8bf6ed96b35933146be/crates/buzz-admin/src/main.rs#L713-L749","documentation":"reconcile-channels rebuilds the kind:39000 channel-metadata addressable event (d tag = channel UUID, plus public/hidden/closed/t tags) and signs it with relay_keys via sign_with_keys. All tags were already parsed successfully with Tag::parse(...)?, so a failure at signing comes from the nostr signing step itself: unusable key material or an event that fails to serialize. The {e} chain identifies the nostr Error variant.","triggerScenarios":"The --relay-key/BUZZ_RELAY_PRIVATE_KEY value parsed as a Keys object but cannot produce a signature (corrupt secret material); or an event-serialization error from malformed tag content that survived parsing.","commonSituations":"Keys that pass a lenient parse but sign incorrectly after a bad key migration; exotic characters in channel_type ending up in the t tag.","solutions":["Read the chained {e} — it names the exact nostr signing error.","Re-verify the signing key: it must be the relay's 32-byte hex/nsec SECRET; re-sign a test event with it in a scratch script to isolate.","Check channel_type and name columns for control characters or invalid UTF-8: SELECT channel_type, name FROM channels WHERE id = '<uuid>';","Re-run reconcile after fixing the key or row data."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// sign a scratch event with the same key first; if this fails, reconcile will too\nlet probe = nostr::EventBuilder::new(nostr::Kind::Custom(0), \"probe\")\n    .sign_with_keys(&relay_keys);\nassert!(probe.is_ok(), \"relay key cannot sign — fix before reconcile\");","typeGuard":null,"tryCatchPattern":"match reconcile_channel(&channel, &relay_keys).await {\n    Ok(_) => {}\n    Err(e) if e.to_string().contains(\"sign kind:39000\") => {\n        // deterministic signing failure: log channel id + e.source() chain, skip channel, continue others\n        skipped.push((channel.id, e.to_string()));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Probe-sign once per run before iterating channels so one bad key stops the job before partial writes.","Keep channel_type/name columns constrained to clean UTF-8 values.","Always pass the stable relay key (--relay-key/env) for reconcile; treat the ephemeral path as bootstrap only."],"tags":["buzz-admin","nostr","event-signing","kind-39000","reconcile-channels"],"backgroundTag":"nostr-event-signing-failed","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"}