{"record":{"id":"2eeb340a1b5626bf","repo":"crowdsecurity/crowdsec","slug":"could-not-delete-alerts-w","errorCode":null,"errorMessage":"could not delete alerts: %w","messagePattern":"could not delete alerts: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/flush.go","lineNumber":385,"sourceCode":"\t\t\treturn fmt.Errorf(\"could not get last alert: %w\", err)\n\t\t}\n\n\t\tif len(lastAlert) != 0 {\n\t\t\tmaxid := lastAlert[0].ID - maxItems\n\n\t\t\tc.Log.Debugf(\"FlushAlerts (max id): %d\", maxid)\n\n\t\t\tif maxid > 0 {\n\t\t\t\t// This may lead to orphan alerts (at least on MySQL), but the next time the flush job will run, they will be deleted\n\t\t\t\t// Alerts that still carry an active decision are kept regardless of the count: deleting them would\n\t\t\t\t// cascade-delete the live decision. They are flushed on a later run, once their decisions expire.\n\t\t\t\tdeletedByNbItem, err = c.Ent.Alert.Delete().Where(\n\t\t\t\t\talert.IDLT(maxid),\n\t\t\t\t\talertWithoutActiveDecision(time.Now().UTC()),\n\t\t\t\t).Exec(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\tc.Log.Errorf(\"FlushAlerts: Could not delete alerts: %s\", err)\n\t\t\t\t\treturn fmt.Errorf(\"could not delete alerts: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif deletedByNbItem > 0 {\n\t\tc.Log.Infof(\"flushed %d/%d alerts because the max number of alerts has been reached (%d max)\",\n\t\t\tdeletedByNbItem, totalAlerts, maxItems)\n\t}\n\n\tif deletedByAge > 0 {\n\t\tc.Log.Infof(\"flushed %d/%d alerts because they were created %s ago or more\",\n\t\t\tdeletedByAge, totalAlerts, maxAge)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/flush.go#L367-L403","documentation":"The ent DELETE removing alerts below the computed max ID (total count minus maxItems) failed in FlushAlerts' max-items branch. The query keeps alerts with active decisions; the failure is at DB execution. Orphan alerts may remain on MySQL — a later flush run cleans them up, which is expected, not data loss.","triggerScenarios":"The `Alert.Delete().Where(alert.IDLT(maxid), alertWithoutActiveDecision(...))` ent mutation fails — DB connection loss, driver-level constraint, or ctx cancellation during the delete.","commonSituations":"Database restarted mid-flush; SQLite lock contention with a concurrently running LAPI write; connection pool exhausted on MySQL/Postgres.","solutions":["Inspect the wrapped driver error in logs for the exact SQL failure","Restore database connectivity/locks and let the next flush cycle retry","If context cancellation, avoid stopping crowdsec during heavy flushes or increase flush frequency so batches stay small","Check disk space on the DB volume — full disk fails deletes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check disk space / connectivity before large flush\nif err := db.Ping(); err != nil { skipFlush = true }","typeGuard":null,"tryCatchPattern":"if err := c.FlushAlerts(ctx, since, maxItems); err != nil {\n    if errors.Is(err, context.Canceled) { return } // shutdown, not a fault\n    scheduleRetry(nextFlushInterval)\n}","preventionTips":["Flush in smaller batches to shorten the delete transaction","Monitor DB health before triggering flushes","Ensure adequate disk space on the database volume"],"tags":["database","ent","delete"],"backgroundTag":"database-write-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}