From 7432464c1fb833c7129a3639c3186f629d002799 Mon Sep 17 00:00:00 2001 From: Taksh Date: Sat, 8 Aug 2026 11:14:27 +0530 Subject: [PATCH] fix(relay): stop panicking the ingest worker on reactions to project events A NIP-25 reaction whose target is a project root or project comment (kind 1621 issue, 1618 PR, or a kind-1 comment on one) carries no h tag, so channel_id is None on the reaction write path. The conformance-trace emission asserted a channel was always present: channel: channel_label(channel_id.expect("reaction path has channel")), so the worker panicked at ingest.rs:2824. The row was inserted before the panic, so the client saw a failed request for a persisted event and retried, and the duplicate branch carried the same expect, head-of-line blocking a durable publish queue forever. Mirror the message write's three-way split at the same seam: (Some, true) -> WriteInsert, (Some, false) -> WriteDuplicate, (None, _) -> WriteInsertGlobal. The conformance vocabulary already models channel-less writes; only the reaction path was missing it. Closes #4936 Signed-off-by: Taksh --- crates/buzz-relay/src/handlers/ingest.rs | 31 ++++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/buzz-relay/src/handlers/ingest.rs b/crates/buzz-relay/src/handlers/ingest.rs index 7aea2ed02e..00211683a8 100644 --- a/crates/buzz-relay/src/handlers/ingest.rs +++ b/crates/buzz-relay/src/handlers/ingest.rs @@ -2837,24 +2837,29 @@ async fn ingest_event_inner( }; let pubkey_hex = auth.pubkey().to_hex(); - // Spec WriteInsert (line 514) / WriteDuplicate (line 606): emit - // the abstract write action. The persist API returns - // `was_inserted` (true → Insert, false → Duplicate). This branch - // is the reaction path; channel_id is always Some here, so - // WriteInsertGlobal does not apply. + // Spec WriteInsert (line 514) / WriteDuplicate (line 606) / + // WriteInsertGlobal (line 559): emit the abstract write action. The + // persist API returns `was_inserted` (true → Insert/Global, false → + // Duplicate). Reactions on project events (issue/PR roots and their + // comments) carry no `h` tag, so `channel_id` can be `None` here — + // mirror the message write's three-way split instead of asserting a + // channel, which panicked the ingest worker on those events. let claimed = claimed_community_from_event(&event); - let action = if was_inserted { - TraceAction::WriteInsert { + let action = match (channel_id, was_inserted) { + (Some(ch), true) => TraceAction::WriteInsert { msg_id: msg_id_label(event.id.as_bytes()), - channel: channel_label(channel_id.expect("reaction path has channel")), + channel: channel_label(ch), claimed_community: claimed, - } - } else { - TraceAction::WriteDuplicate { + }, + (Some(ch), false) => TraceAction::WriteDuplicate { msg_id: msg_id_label(event.id.as_bytes()), - channel: channel_label(channel_id.expect("reaction path has channel")), + channel: channel_label(ch), claimed_community: claimed, - } + }, + (None, _) => TraceAction::WriteInsertGlobal { + msg_id: msg_id_label(event.id.as_bytes()), + claimed_community: claimed, + }, }; emit(tracer, action, state_for_request(tenant, auth.pubkey())); dispatch_persistent_event(