block/buzz · error
ban failed: {e}
Error message
ban failed: {e} What it means
The 'ban' arm of `run_atomic_mutation` wraps any failure from `state.db.execute_ban_with_marker` with 'ban failed: {e}'. The context (target was present, lease held) was valid, but the database-level ban operation itself returned an error, which is re-wrapped for the enforcement driver to record.
Source
Thrown at crates/buzz-relay/src/handlers/report_resolution.rs:699
// ownership fence rejected the transaction (lease lost or marker already set
// by a concurrent driver). We classify Ok(false) by reloading the row.
let raw: anyhow::Result<bool> = match ctx.action {
"ban" => {
let target = ctx
.target_pubkey
.ok_or_else(|| anyhow::anyhow!("ban requires target_pubkey"))?;
state
.db
.execute_ban_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
ctx.actor_pubkey,
ctx.reason,
)
.await
.map_err(|e| anyhow::anyhow!("ban failed: {e}"))
}
"timeout" => {
let target = ctx
.target_pubkey
.ok_or_else(|| anyhow::anyhow!("timeout requires target_pubkey"))?;
let until = ctx
.timeout_until
.ok_or_else(|| anyhow::anyhow!("timeout requires timeout_until"))?;
state
.db
.execute_timeout_with_marker(
action_id,
lease_token,
ctx.community_id,
target,
ctx.actor_pubkey,
until,
ctx.reason,View on GitHub (pinned to eed74bde2f)
Solutions
- Read the inner `{e}` message in the error chain to identify the DB-level cause (constraint, lease conflict, connectivity).
- If it is a lease/marker conflict, the action was already processed — treat Ok(false)/duplicate as expected and skip.
- Check Postgres health and migrations (`just test` integration suite reproduces DB-dependent failures locally).
- Retry the enforcement driver after transient DB errors; the atomic marker design makes ban idempotent per action_id.
Example fix
// before
execute_ban_with_marker(...).await.map_err(|e| anyhow!("ban failed: {e}"))
// after — tolerate already-applied marker
match execute_ban_with_marker(...).await {
Ok(applied) => Ok(applied),
Err(e) if e.to_string().contains("marker already set") => Ok(false),
Err(e) => Err(anyhow!("ban failed: {e}")),
} Defensive patterns
Strategy: retry
Validate before calling
anyhow::ensure!(ctx.target_pubkey.is_some(), "refusing to enqueue ban without target"); anyhow::ensure!(state.db.ping().await.is_ok(), "postgres unreachable before ban");
Try / catch
for attempt in 0..3 {
match run_atomic_mutation(&state, ctx).await {
Ok(applied) => break Ok(applied),
Err(e) if e.to_string().contains("ban failed") && attempt < 2 && is_transient(&e) => {
tokio::time::sleep(backoff(attempt)).await; // marker design makes retry safe
}
Err(e) => break Err(e),
}
} Prevention
- Back ban writes with the idempotent action marker and retry only transient DB errors.
- Monitor Postgres health; alert on constraint/deadlock errors from the enforcement driver.
- Keep relay and DB schema migrations in lockstep.
- Log the full anyhow error chain so the inner DB cause is visible.
When it happens
Trigger: Calling `execute_ban_with_marker` for an action where the DB operation fails: lease/marker ownership conflict, community or user row missing, foreign-key violation, deadlock, or Postgres connectivity failure during the atomic mutation.
Common situations: Two enforcement drivers racing on the same action (marker already set); target user or community id no longer exists in the DB; transient Postgres connection drops under load; schema migration drift between the relay and the database.
Related errors
- timeout failed: {e}
- ban requires target_pubkey
- timeout requires target_pubkey
- timeout requires timeout_until
- kick requires target_pubkey
AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30).
Data as JSON: /api/errors/85b690070ed09065.
Report an issue: GitHub.