block/buzz · error
kick requires channel_id
Error message
kick requires channel_id
What it means
`run_atomic_mutation` was asked to execute a "kick" enforcement action, but `EnforcementCtx.channel_id` was `None`. A kick removes a member from a specific NIP-29 channel, so the handler cannot proceed without knowing which channel to remove the target from. This is an internal invariant failure: the enforcement-context builder (shared by the HTTP driver and the recovery worker via `drive_enforcement`) failed to populate the channel for a kick action.
Source
Thrown at crates/buzz-relay/src/handlers/report_resolution.rs:728
.execute_timeout_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
ctx.actor_pubkey,
until,
ctx.reason,
)
.await
.map_err(|e| anyhow::anyhow!("timeout failed: {e}"))
}
"kick" => {
let target = ctx
.target_pubkey
.ok_or_else(|| anyhow::anyhow!("kick requires target_pubkey"))?;
let ch = ctx
.channel_id
.ok_or_else(|| anyhow::anyhow!("kick requires channel_id"))?;
match state
.db
.execute_kick_with_marker(
action_id,
lease_token,
ctx.community_id,
ch,
target,
ctx.actor_pubkey,
)
.await
.map_err(|e| anyhow::anyhow!("kick failed: {e}"))?
{
buzz_db::relay_admin_actions::KickWithMarkerResult::Removed => Ok(true),
buzz_db::relay_admin_actions::KickWithMarkerResult::AlreadyMarked => Ok(false),
buzz_db::relay_admin_actions::KickWithMarkerResult::AlreadyGone => Err(
anyhow::anyhow!("kick target was already absent before this action"),
),View on GitHub (pinned to eed74bde2f)
Solutions
- Check how the report's target was decoded: ensure the reported target carries a channel (`h` tag) and that the context builder sets `ctx.channel_id` for kick actions.
- Guard the action builder: refuse to create a kick `EnforcementCtx` (or persist the admin action row) when the channel cannot be derived — fail earlier with a clearer message.
- If the data is missing, re-derive the channel from the reported event's `h` tag or thread-root metadata and re-trigger enforcement.
- For hand-inserted recovery rows, delete/re-create the admin action row against a report with proper channel scoping.
Example fix
// before
let ctx = EnforcementCtx {
action: "kick",
target_pubkey,
channel_id: None, // kick now fails
..base
};
// after
let channel_id = channel_id
.or_else(|| derive_channel_from_event(&event))
.ok_or_else(|| anyhow::anyhow!("kick action needs a channel; report target has no h tag"))?;
let ctx = EnforcementCtx { action: "kick", target_pubkey, channel_id: Some(channel_id), ..base }; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_kick_ctx(ctx: &EnforcementCtx) -> anyhow::Result<&EnforcementCtx<'_>> {
anyhow::ensure!(ctx.action != "kick" || ctx.channel_id.is_some(),
"kick enforcement requires a channel_id; report target must carry an h tag");
anyhow::ensure!(ctx.action != "kick" || ctx.target_pubkey.is_some(),
"kick enforcement requires a target_pubkey");
Ok(ctx)
}
// call before drive_enforcement:
// ensure_kick_ctx(&ctx)?; Type guard
fn kick_ctx_is_complete(ctx: &EnforcementCtx<'_>) -> bool {
ctx.action != "kick"
|| (ctx.target_pubkey.is_some() && ctx.channel_id.is_some())
} Prevention
- Only offer "kick" resolutions on reports whose target includes a channel `h` tag.
- Validate EnforcementCtx completeness at action-creation time, before persisting the admin action row.
- Add a unit test asserting drive_enforcement never receives a kick ctx with None channel_id.
- Keep derive_enforcement_target_pub as the single source of truth for target fields.
When it happens
Trigger: 1) The report's target decode produced no channel id (e.g. target kind carries no `h`/channel tag) but was resolved with a kick. 2) A code path constructed `EnforcementCtx` manually for a kick without setting `channel_id`. 3) Legacy admin-action rows referencing reports whose channel scoping was never recorded.
Common situations: Resolving a report with kick in a deployment whose DB rows predate channel-scoped report targets; custom forks of `derive_enforcement_target_pub`/context building that forget `channel_id`; hand-written recovery tooling inserting kick action rows without a channel.
Related errors
- delete requires target_event_id
- kick target was already absent before this action
- actor is not an active member
- cannot remove the last owner
- invalid URL {url:?}: {e}
AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30).
Data as JSON: /api/errors/bc9fb2780b64891c.
Report an issue: GitHub.