block/buzz · error
sign roster
Error message
sign roster
What it means
This panic message comes from `EventBuilder::sign_with_keys(...).expect("sign roster")` inside a test helper that builds a kind:39002 (community roster / membership) event signed with the relay keys. It fires when the builder cannot produce a signed Nostr event — almost always because the key material is invalid or a required tag/header is missing. The library throws it to fail the test immediately rather than return an untestable Result.
Source
Thrown at crates/buzz-db/src/store/channel_members.rs:3179
.bind(channel)
.bind(member.as_slice())
.bind(owner.as_slice())
.execute(&pool)
.await
.expect("seed canonical admin");
let roster = |role: &str, timestamp| {
EventBuilder::new(Kind::Custom(39002), "")
.tags(vec![
Tag::parse(["d", channel.to_string().as_str()]).expect("d tag"),
Tag::parse(["p", hex::encode(owner).as_str(), "", "owner"])
.expect("owner p tag"),
Tag::parse(["p", hex::encode(member).as_str(), "", role])
.expect("member p tag"),
])
.custom_created_at(Timestamp::from(timestamp))
.sign_with_keys(&relay_keys)
.expect("sign roster")
};
let base = Timestamp::now().as_secs();
let fresh = roster("admin", base);
assert!(
db.replace_addressable_event(community, &fresh, Some(channel))
.await
.expect("publish canonical role")
.1
);
let stale = roster("member", base + 1);
let error = db
.replace_addressable_event(community, &stale, Some(channel))
.await
.expect_err("desired-state fence must reject stale role");
assert!(matches!(
error,
DbError::Sqlx(sqlx::Error::Database(ref db_error))
if db_error.code().as_deref() == Some("23514")View on GitHub (pinned to dad5a33865)
Solutions
- Check how `relay_keys` is constructed in this test file and confirm it holds valid KeyPair/Keys (e.g. Keys::generate() or a valid hex secret).
- Run the failing test with RUST_BACKTRACE=1 to see whether the panic originates in key parsing or in event signing.
- Verify the Tag::parse calls above it succeed — a bad tag list can leave the builder in a bad state; add distinct expect messages to isolate which call panics.
- If keys are loaded from env/config, validate them once in test setup and fall back to Keys::generate() for scratch tests.
Example fix
// before
let relay_keys = Keys::parse(env_key_hex).expect("relay keys");
// after
let relay_keys = match env_key_hex {
Ok(h) => Keys::parse(h).expect("valid relay key hex"),
Err(_) => Keys::generate(), // scratch test does not need a fixed key
}; Defensive patterns
Strategy: validation
Validate before calling
// validate keys before building/signing the roster event
let secret = relay_keys.secret_key().expect("relay keys have a secret key");
assert_eq!(secret.display_secret().len(), 64, "secret key must be 32-byte hex"); Type guard
fn has_signable_keys(keys: &Keys) -> bool {
keys.secret_key().map(|s| s.display_secret().len() == 64).unwrap_or(false)
} Prevention
- Generate test keys with Keys::generate() instead of parsing hand-written hex.
- Give every expect/unwrap in fixtures a distinct message so the failing call is obvious.
- Validate key material once in a shared test setup helper.
- Keep EventBuilder tag construction in a small typed helper checked by its own unit test.
When it happens
Trigger: Calling `sign_with_keys(&relay_keys)` on an EventBuilder whose keys cannot sign (malformed/empty SecretKey in `relay_keys`), or when event construction with `.tags([...]).custom_created_at(...)` produced an internally inconsistent event that signing rejects.
Common situations: Test fixtures where `relay_keys` was generated or parsed incorrectly (e.g. hex-decoded from an invalid string), refactors that changed EventBuilder tag APIs so a tag list is malformed, or deterministic-timestamp helpers constructing Timestamp::from(0) values rejected by validation.
Related errors
- failed to sign moderation notice: {e}
- failed to sign system message: {e}
- failed to sign member snapshot: {error}
- connect to test DB
- moderation notice recipient must be a 32-byte pubkey, got {}
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30).
Data as JSON: /api/errors/c1c50f0b6da4a127.
Report an issue: GitHub.