crowdsecurity/crowdsec · error · DeleteFail

alert graph delete batch: %w

Error message

alert graph delete batch: %w

What it means

In DeleteAlertGraphBatch, after the associated decisions are removed, the alerts themselves are bulk-deleted. This error means that final Alert.Delete() call failed, so the alerts (and possibly already-deleted decisions) are in a partially cleaned state. It wraps the DeleteFail sentinel indicating a database delete failure.

Source

Thrown at pkg/database/alerts.go:925

	_, err = c.Ent.Meta.Delete().
		Where(meta.HasOwnerWith(alert.IDIn(idList...))).Exec(ctx)
	if err != nil {
		c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
		return 0, fmt.Errorf("alert graph delete batch meta: %w", DeleteFail)
	}

	_, err = c.Ent.Decision.Delete().
		Where(decision.HasOwnerWith(alert.IDIn(idList...))).Exec(ctx)
	if err != nil {
		c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
		return 0, fmt.Errorf("alert graph delete batch decisions: %w", DeleteFail)
	}

	deleted, err := c.Ent.Alert.Delete().
		Where(alert.IDIn(idList...)).Exec(ctx)
	if err != nil {
		c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
		return deleted, fmt.Errorf("alert graph delete batch: %w", DeleteFail)
	}

	c.Log.Debug("Done batch delete alerts")

	return deleted, nil
}

func (c *Client) DeleteAlertGraph(ctx context.Context, alertItem *ent.Alert) error {
	// delete the associated events
	_, err := c.Ent.Event.Delete().
		Where(event.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(ctx)
	if err != nil {
		c.Log.Warningf("DeleteAlertGraph : %s", err)
		return fmt.Errorf("event with alert ID '%d': %w", alertItem.ID, DeleteFail)
	}

	// delete the associated meta
	_, err = c.Ent.Meta.Delete().

View on GitHub (pinned to 909b515798)

Solutions

  1. Inspect the warning log 'DeleteAlertGraphBatch : %s' for the underlying DB error
  2. Retry the batch delete — decisions were already removed, remaining alerts should delete cleanly
  3. Check DB grants and connection stability
  4. Reduce batch size to avoid long-running transactions/locks

Example fix

// before
deleted, err := client.DeleteAlertGraphBatch(ctx, ids)
// after
deleted, err := client.DeleteAlertGraphBatch(ctx, ids)
if err != nil && errors.Is(err, entdb.DeleteFail) {
    log.Warnf("partial delete: %d alerts affected, retry", deleted)
}
Defensive patterns

Strategy: retry

Validate before calling

if err := client.Ent.Alert.Query().Where(alert.IDIn(ids...)).Exist(ctx); err != nil {
    return fmt.Errorf("cannot reach alerts table: %w", err)
}

Try / catch

deleted, err := client.DeleteAlertGraphBatch(ctx, ids)
if errors.Is(err, entdb.DeleteFail) {
    // decisions may already be gone; safe to retry the batch
    deleted, err = client.DeleteAlertGraphBatch(ctx, ids)
}

Prevention

When it happens

Trigger: c.DeleteAlertGraphBatch(ctx, idList) is called, decision deletion succeeded, but Alert.Delete().Where(alert.IDIn(idList...)) fails: connection loss between the two statements, lock contention on alerts, insufficient DELETE grants, or context cancellation.

Common situations: Large batch deletes timing out under lock contention while a machine still streams alerts; DB failover mid-request on LAPI; low-privileged DB user missing DELETE on alerts.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/4b03ee63ffc93895. Report an issue: GitHub.