block/buzz · error
publish canonical role
Error message
publish canonical role
What it means
Panic from `db.replace_addressable_event(community, &fresh, Some(channel)).await.expect("publish canonical role")`. The test asserts that a fresh kind:39002 roster event (newer created_at) is accepted via last-write-wins (NIP-33 style) replacement; the expect fires when the DB call returns Err instead of the expected (result, replaced=true) tuple.
Source
Thrown at crates/buzz-db/src/store/channel_members.rs:3186
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")
));
let live_id: Vec<u8> = sqlx::query_scalar(
"SELECT id FROM events WHERE community_id=$1 AND channel_id=$2 \
AND kind=39002 AND deleted_at IS NULL",
)
.bind(community_uuid)
.bind(channel)View on GitHub (pinned to dad5a33865)
Solutions
- Ensure TEST_DATABASE_URL points at a running Postgres with current migrations (just setup / just test).
- Check the scratch DB setup/teardown helpers in this test file — the community and channel rows must exist before publishing the roster.
- Confirm replace_addressable_event still accepts kind:39002 events with custom created_at after any LWW-related changes.
- Run with RUST_BACKTRACE=1 and the sqlx query log to see the underlying DB error.
Example fix
// before
assert!(db.replace_addressable_event(community, &fresh, Some(channel)).await.expect("publish canonical role").1);
// after
let res = db.replace_addressable_event(community, &fresh, Some(channel)).await
.unwrap_or_else(|e| panic!("publish canonical role: {e:?}"));
assert!(res.1, "expected LWW replacement of existing roster"); Defensive patterns
Strategy: try-catch
Validate before calling
// before publishing, confirm prerequisites exist
let community_row = sqlx::query("SELECT 1 FROM communities WHERE id=$1").bind(community).fetch_optional(&pool).await.expect("check community");
assert!(community_row.is_some(), "community row must exist before roster publish"); Try / catch
match db.replace_addressable_event(community, &fresh, Some(channel)).await {
Ok((_, true)) => {}, // replaced as expected
Ok((_, false)) => panic!("fresh roster was not treated as LWW winner"),
Err(e) => panic!("publish canonical role failed: {e}"),
} Prevention
- Run integration tests via `just test` so Postgres and migrations are provisioned.
- Create community/channel prerequisite rows in setup, not mid-test.
- Log the underlying sqlx error instead of a bare expect message.
- After changing LWW logic, re-run replace_addressable_event tests before the full suite.
When it happens
Trigger: `replace_addressable_event` errors when the addressable event fails verification, the community/channel row does not exist, the scratch database schema/migrations are stale, or a database connection error occurs (TEST_DATABASE_URL unreachable).
Common situations: Running integration tests without Postgres provisioned, migrations not applied to the scratch DB, or a code change to replace_addressable_event that now rejects valid roster events.
Related errors
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30).
Data as JSON: /api/errors/836d0b84edce2a75.
Report an issue: GitHub.