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

  1. Read the inner `{e}` message in the error chain to identify the DB-level cause (constraint, lease conflict, connectivity).
  2. If it is a lease/marker conflict, the action was already processed — treat Ok(false)/duplicate as expected and skip.
  3. Check Postgres health and migrations (`just test` integration suite reproduces DB-dependent failures locally).
  4. 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

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


AI-assisted analysis of block/buzz@eed74bde2f (2026-08-30). Data as JSON: /api/errors/85b690070ed09065. Report an issue: GitHub.