crowdsecurity/crowdsec · error

hard delete decisions with provided filter: %w

Error message

hard delete decisions with provided filter: %w

What it means

deleteDecisionBatch is the batched worker used by DeleteDecisions for hard deletions: it runs an ent Delete().Where(IDIn(ids...)).Exec(ctx). If the DELETE statement fails, the raw DB error is wrapped with this message, meaning rows could not be removed from the database.

Source

Thrown at pkg/database/decisions.go:336

			return err
		}
		total += rows
		return nil
	})

	return total, err
}

// deleteDecisionBatch removes the decisions as a single operation.
func (c *Client) deleteDecisionBatch(ctx context.Context, batch []*ent.Decision) (int, error) {
	ids := decisionIDs(batch)

	rows, err := c.Ent.Decision.
		Delete().
		Where(decision.IDIn(ids...)).
		Exec(ctx)
	if err != nil {
		return 0, fmt.Errorf("hard delete decisions with provided filter: %w", err)
	}

	return rows, nil
}

// DeleteDecisions removes a list of decisions from the database,
// in multiple operations if len(decisions) > decisionDeleteBulkSize.
// It returns the number of impacted decisions for the CAPI/PAPI, even in case of error.
func (c *Client) DeleteDecisions(ctx context.Context, decisions []*ent.Decision) (int, error) {
	if len(decisions) == 0 {
		return 0, nil
	}

	total := 0
	err := slicetools.Batch(ctx, decisions, decisionDeleteBulkSize, func(ctx context.Context, batch []*ent.Decision) error {
		rows, err := c.deleteDecisionBatch(ctx, batch)
		if err != nil {
			return err

View on GitHub (pinned to 909b515798)

Solutions

  1. Verify the DB is writable by the crowdsec user and not locked by another process
  2. Retry; batches are independent so already-deleted rows are simply gone
  3. Reduce batch size to shorten each transaction and lower contention
  4. Check the wrapped inner error for the exact SQL/driver failure

Example fix

// before
// running: crowdsec with a read-only sqlite file
// after
# chown crowdsec:crowdsec /var/lib/crowdsec/data/crowdsec.db && chmod u+w /var/lib/crowdsec/data/crowdsec.db
Defensive patterns

Strategy: retry

Validate before calling

if len(ids) == 0 { return nil }
if !dbWritable() { return errors.New("database is read-only") }

Try / catch

if _, err := client.DeleteDecisions(ctx, decisions, hardDelete); err != nil {
    if strings.Contains(err.Error(), "hard delete decisions with provided filter") {
        // inspect wrapped DB error; retry after checking locks/permissions
    }
    return err
}

Prevention

When it happens

Trigger: Ent Delete() failing for a batch of decision IDs: DB unreachable, foreign-key or trigger errors, context cancelled, lock contention with concurrent writers.

Common situations: cscli decisions delete --all racing with an active import; SQLite locked by another process; DB read-only (permissions) or disk full.

Related errors


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