crowdsecurity/crowdsec · error

unable to flush alerts older than %s: %w

Error message

unable to flush alerts older than %s: %w

What it means

The ent DELETE flushing alerts older than maxAge failed in FlushAlerts' max-age branch. The query deliberately keeps alerts that still have an active decision; the failure is at DB execution. A warning is logged alongside and the error propagates to the flush-job caller.

Source

Thrown at pkg/database/flush.go:344

	if err != nil {
		c.Log.Warningf("FlushAlerts (max items count): %s", err)
		return fmt.Errorf("unable to get alerts count: %w", err)
	}

	c.Log.Debugf("FlushAlerts (Total alerts): %d", totalAlerts)

	if maxAge != 0 {
		now := time.Now().UTC()

		// Delete alerts older than maxAge, but never one that still has an
		// active decision (the cascade would take the live decision with it).
		nbDeleted, err := c.Ent.Alert.Delete().Where(
			alert.CreatedAtLTE(now.Add(-maxAge)),
			alertWithoutActiveDecision(now),
		).Exec(ctx)
		if err != nil {
			c.Log.Warningf("FlushAlerts (max age): %s", err)
			return fmt.Errorf("unable to flush alerts older than %s: %w", maxAge, err)
		}

		c.Log.Debugf("FlushAlerts (deleted max age alerts): %d", nbDeleted)
		deletedByAge = nbDeleted
	}

	if maxItems > 0 {
		// We get the highest id for the alerts
		// We subtract MaxItems to avoid deleting alerts that are not old enough
		// This gives us the oldest alert that we want to keep
		// We then delete all the alerts with an id lower than this one
		// We can do this because the id is auto-increment, and the database won't reuse the same id twice
		lastAlert, err := c.QueryAlertWithFilter(ctx, map[string][]string{
			"sort":  {"DESC"},
			"limit": {"1"},
			// we do not care about fetching the edges, we just want the id
			"with_decisions": {"false"},
		})

View on GitHub (pinned to 909b515798)

Solutions

  1. Read the wrapped error: for lock contention on SQLite, stop concurrent cscli usage or switch to Postgres/MySQL for busy installs.
  2. For timeouts on large tables, run the flush when the system is quiet, or prune manually with cscli alerts delete --until.
  3. Check DB connectivity and server logs if the connection was dropped mid-query.
  4. Ensure the schema matches the binary version (restart crowdsec / run migrations) after upgrades.
  5. Retry — the next scheduler run in one minute will attempt the flush again.

Example fix

// manual relief when the automated flush keeps timing out on a huge table
cscli alerts delete --until 2025-01-01T00:00:00Z --batch 1000
// then restart crowdsec so the scheduler resumes with a smaller backlog
Defensive patterns

Strategy: try-catch

Validate before calling

if err := c.Ent.Alert.Query().Limit(1).Exec(ctx); err != nil {
    return fmt.Errorf("database unreachable, skipping flush: %w", err)
}

Type guard

if c == nil || c.Ent == nil {
    return errors.New("database client not initialized")
}

Try / catch

if err := c.FlushAlerts(ctx, maxAge, maxItems); err != nil {
    if strings.Contains(err.Error(), "unable to flush alerts older than") {
        // transient DB failure; scheduler retries next minute
        c.Log.Warningf("alert flush failed, will retry: %s", err)
    }
}

Prevention

When it happens

Trigger: c.Ent.Alert.Delete().Where(...).Exec(ctx) fails: DB connection lost mid-transaction, context canceled/timeout, SQLite lock contention, or schema incompatibility between the ent code and the live DB.

Common situations: Long-running flush competing with a locked SQLite file during a cscli backup; PostgreSQL connection dropped by a proxy idle timeout; context deadline exceeded when the alerts table is very large and the DELETE exceeds a configured timeout.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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