{"record":{"id":"c1c0acf2172ddfcc","repo":"crowdsecurity/crowdsec","slug":"unable-to-flush-alerts-older-than-s-w","errorCode":null,"errorMessage":"unable to flush alerts older than %s: %w","messagePattern":"unable to flush alerts older than (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/flush.go","lineNumber":344,"sourceCode":"\tif err != nil {\n\t\tc.Log.Warningf(\"FlushAlerts (max items count): %s\", err)\n\t\treturn fmt.Errorf(\"unable to get alerts count: %w\", err)\n\t}\n\n\tc.Log.Debugf(\"FlushAlerts (Total alerts): %d\", totalAlerts)\n\n\tif maxAge != 0 {\n\t\tnow := time.Now().UTC()\n\n\t\t// Delete alerts older than maxAge, but never one that still has an\n\t\t// active decision (the cascade would take the live decision with it).\n\t\tnbDeleted, err := c.Ent.Alert.Delete().Where(\n\t\t\talert.CreatedAtLTE(now.Add(-maxAge)),\n\t\t\talertWithoutActiveDecision(now),\n\t\t).Exec(ctx)\n\t\tif err != nil {\n\t\t\tc.Log.Warningf(\"FlushAlerts (max age): %s\", err)\n\t\t\treturn fmt.Errorf(\"unable to flush alerts older than %s: %w\", maxAge, err)\n\t\t}\n\n\t\tc.Log.Debugf(\"FlushAlerts (deleted max age alerts): %d\", nbDeleted)\n\t\tdeletedByAge = nbDeleted\n\t}\n\n\tif maxItems > 0 {\n\t\t// We get the highest id for the alerts\n\t\t// We subtract MaxItems to avoid deleting alerts that are not old enough\n\t\t// This gives us the oldest alert that we want to keep\n\t\t// We then delete all the alerts with an id lower than this one\n\t\t// We can do this because the id is auto-increment, and the database won't reuse the same id twice\n\t\tlastAlert, err := c.QueryAlertWithFilter(ctx, map[string][]string{\n\t\t\t\"sort\":  {\"DESC\"},\n\t\t\t\"limit\": {\"1\"},\n\t\t\t// we do not care about fetching the edges, we just want the id\n\t\t\t\"with_decisions\": {\"false\"},\n\t\t})","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/flush.go#L326-L362","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the wrapped error: for lock contention on SQLite, stop concurrent cscli usage or switch to Postgres/MySQL for busy installs.","For timeouts on large tables, run the flush when the system is quiet, or prune manually with cscli alerts delete --until.","Check DB connectivity and server logs if the connection was dropped mid-query.","Ensure the schema matches the binary version (restart crowdsec / run migrations) after upgrades.","Retry — the next scheduler run in one minute will attempt the flush again."],"exampleFix":"// manual relief when the automated flush keeps timing out on a huge table\ncscli alerts delete --until 2025-01-01T00:00:00Z --batch 1000\n// then restart crowdsec so the scheduler resumes with a smaller backlog","handlingStrategy":"try-catch","validationCode":"if err := c.Ent.Alert.Query().Limit(1).Exec(ctx); err != nil {\n    return fmt.Errorf(\"database unreachable, skipping flush: %w\", err)\n}","typeGuard":"if c == nil || c.Ent == nil {\n    return errors.New(\"database client not initialized\")\n}","tryCatchPattern":"if err := c.FlushAlerts(ctx, maxAge, maxItems); err != nil {\n    if strings.Contains(err.Error(), \"unable to flush alerts older than\") {\n        // transient DB failure; scheduler retries next minute\n        c.Log.Warningf(\"alert flush failed, will retry: %s\", err)\n    }\n}","preventionTips":["Keep the alerts table small so DELETEs stay short — a healthy max_age helps.","For large installs use Postgres/MySQL instead of SQLite to avoid lock contention.","Do not cancel the scheduler context abruptly mid-flush; use graceful shutdown.","Apply schema migrations before restarting the service."],"tags":["go","database","crowdsec","flush"],"backgroundTag":"database-query-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}