block/buzz · error
unexpected enforcement action: {other}
Error message
unexpected enforcement action: {other} What it means
This is a dispatch exhaustiveness error in `run_atomic_mutation`: the enforcement action string in `ctx.action` matched none of the supported arms ("ban", "timeout", "kick", "delete"). It indicates the action record carries an unrecognized or corrupt action type, so the driver refuses to execute an unknown mutation rather than guessing. The `{other}` placeholder names the unexpected action value.
Source
Thrown at crates/buzz-relay/src/handlers/report_resolution.rs:773
.get_thread_metadata_by_event(ctx.community_id, target)
.await
.map_err(|e| anyhow::anyhow!("thread metadata lookup failed: {e}"))?;
let parent_id = meta.as_ref().and_then(|m| m.parent_event_id.clone());
let root_id = meta.as_ref().and_then(|m| m.root_event_id.clone());
state
.db
.execute_delete_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
parent_id.as_deref(),
root_id.as_deref(),
)
.await
.map_err(|e| anyhow::anyhow!("delete failed: {e}"))
}
other => Err(anyhow::anyhow!("unexpected enforcement action: {other}")),
};
match raw? {
true => Ok(MutationOutcome::Committed),
false => {
// Reload to distinguish "step_marker already set by another driver"
// (AlreadyCommitted — safe to proceed to finalization) from "this
// driver's lease expired" (LeaseLost — must stop, recovery worker
// will take over after expiry).
let rec = state
.db
.get_admin_action(action_id)
.await
.map_err(|e| anyhow::anyhow!("classify mutation result: {e}"))?;
match rec {
Some(r) if r.step_marker.is_some() => Ok(MutationOutcome::AlreadyCommitted),
_ => Ok(MutationOutcome::LeaseLost),
}View on GitHub (pinned to eed74bde2f)
Solutions
- Log/inspect the offending action value in `{other}` and find the admin_actions row that produced it.
- Add a match arm for the new action type if it is a legitimately supported action.
- Delete or correct the corrupt action row (and its report linkage) so the recovery worker stops re-driving it.
- Ensure relay and recovery-worker binaries are the same version so action strings and match arms agree.
- Validate the action string at action-creation time so unknown values are rejected before persistence.
Example fix
// before
other => Err(anyhow::anyhow!("unexpected enforcement action: {other}")),
// after: validate at creation + exhaustive arm
if !matches!(action, "ban" | "timeout" | "kick" | "delete") {
return Err(anyhow::anyhow!("unsupported action at creation: {action}"));
}
// ...and in the match:
"mute" => execute_mute_with_marker(...).map_err(|e| anyhow::anyhow!("mute failed: {e}")),
other => Err(anyhow::anyhow!("unexpected enforcement action: {other}")), Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED_ACTIONS: &[&str] = &["ban", "timeout", "kick", "delete"];
fn is_supported_action(action: &str) -> bool { SUPPORTED_ACTIONS.contains(&action) }
// call before driving: assert!(is_supported_action(&ctx.action)); Type guard
fn parse_enforcement_action(s: &str) -> Option<EnforcementAction> {
match s {
"ban" => Some(EnforcementAction::Ban),
"timeout" => Some(EnforcementAction::Timeout),
"kick" => Some(EnforcementAction::Kick),
"delete" => Some(EnforcementAction::Delete),
_ => None,
}
} Try / catch
match parse_enforcement_action(&ctx.action) {
Some(action) => drive(action),
None => {
warn!("skipping unknown enforcement action '{}' for action_id {}", ctx.action, ctx.action_id);
mark_action_poisoned(ctx.action_id); // so recovery worker stops re-driving
}
} Prevention
- Model actions as an enum serialized to the DB instead of free-form strings so invalid values cannot be persisted.
- Validate the action string at admin-action creation time.
- Keep relay and recovery-worker deployments version-aligned.
- Audit admin_actions rows after schema/migrations for stray action values.
When it happens
Trigger: An admin_actions row whose action field is something other than ban/timeout/kick/delete is fed into `drive_enforcement` — e.g. a new action kind added to the actions table without a matching match arm, a typo'd action string written at creation, or an old row from a schema/migration change.
Common situations: A code version mismatch where a newer relay wrote a new action type but an older binary (or the recovery worker) drives it; manual DB edits/seed rows with invalid action values; a regression in the code path that constructs EnforcementContext.
Related errors
- ban requires target_pubkey
- timeout requires target_pubkey
- kick requires target_pubkey
- ban failed: {e}
- timeout requires timeout_until
AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30).
Data as JSON: /api/errors/335ec799ea77a116.
Report an issue: GitHub.