block/buzz · error

delete failed: {e}

Error message

delete failed: {e}

What it means

This error wraps a database failure from `execute_delete_with_marker` during the "delete" enforcement action of report resolution (`run_atomic_mutation`). The atomic mutation tries to delete the reported event and set the action's step_marker in one lease-fenced transaction; if the DB layer returns any error (connection issue, constraint violation, row gone), it is re-wrapped with the "delete failed: {e}" context and propagated up through `drive_enforcement`. The inner `{e}` carries the actual Postgres/DB-layer cause.

Source

Thrown at crates/buzz-relay/src/handlers/report_resolution.rs:771

            let meta = state
                .db
                .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),

View on GitHub (pinned to eed74bde2f)

Solutions

  1. Check the inner `{e}` message to identify the actual DB-layer cause (connectivity vs constraint vs serialization) and fix that root problem.
  2. Verify Postgres connectivity/health of the relay's database pool (connections, timeouts, restarts).
  3. Retry the action after the lease expires — the action recovery worker will re-drive the mutation; the lease-fenced marker makes retries safe.
  4. If a constraint violation on parent/root, inspect `thread_metadata` rows for the target event and confirm parent/root ids match the live thread state.
  5. Check relay logs around `drive_enforcement` for whether `raw?` failed at the mutation or the later classification step.

Example fix

// before: raw anyhow error from DB layer bubbles with no context
.execute_delete_with_marker(...).await?;
// after: context-wrapped (as thrown) — fix at call site by matching recoverable DB errors
let deleted = state.db.execute_delete_with_marker(...).await
    .map_err(|e| anyhow::anyhow!("delete failed: {e}"));
match deleted {
    Ok(v) => Ok(v),
    Err(e) if is_transient_db_error(&e) => Ok(false), // let recovery worker re-drive
    Err(e) => Err(anyhow::anyhow!("delete failed: {e}")),
}
Defensive patterns

Strategy: retry

Validate before calling

// before driving: confirm target event still exists and DB is reachable
let meta = state.db.get_thread_metadata_by_event(ctx.community_id, target).await
    .map_err(|e| anyhow::anyhow!("pre-check thread metadata: {e}"))?;
if is_transient_db_error(&e) { schedule_retry_after_lease_expiry(action_id); }

Type guard

fn is_transient_db_error(e: &anyhow::Error) -> bool {
    let s = format!("{e:#}");
    ["connection", "timeout", "closed", "pool"].iter().any(|k| s.contains(k))
}

Try / catch

match run_atomic_mutation(ctx).await {
    Ok(outcome) => proceed_with_finalization(outcome),
    Err(e) if is_transient_db_error(&e) => log_and_defer_to_recovery_worker(ctx.action_id, e),
    Err(e) => return Err(e), // permanent: surface to moderator
}

Prevention

When it happens

Trigger: Calling resolve/enforcement flow where ctx.action == "delete" and `execute_delete_with_marker(action_id, lease_token, community_id, target_event_id, parent_id, root_id)` returns Err — e.g. Postgres connection dropped, deadlock, the target event row violating a FK during deletion, or a transaction failure inside the lease-fenced marker update.

Common situations: Relay DB briefly unavailable or failing over while a moderator resolves a report with 'delete content'; recovery worker re-driving an action against a DB under lock contention; parent/root thread metadata rows being concurrently mutated causing FK conflicts during thread-aware delete.

Related errors


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