{"record":{"id":"f278824a45c9a570","repo":"crowdsecurity/crowdsec","slug":"could-not-get-last-alert-w","errorCode":null,"errorMessage":"could not get last alert: %w","messagePattern":"could not get last alert: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/flush.go","lineNumber":367,"sourceCode":"\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})\n\t\tc.Log.Debugf(\"FlushAlerts (last alert): %+v\", lastAlert)\n\n\t\tif err != nil {\n\t\t\tc.Log.Errorf(\"FlushAlerts: could not get last alert: %s\", err)\n\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)","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/flush.go#L349-L385","documentation":"FlushAlerts wraps an error returned while querying the most recent alert from the alerts table. The library throws it when the ent query used to determine the flush boundary (max-age or max-items) fails, aborting the whole flush operation.","triggerScenarios":"The ent Alert query used to fetch the last alert returns an error — typically a database connectivity loss, closed/locked SQLite file, or context cancellation while FlushAlerts runs its maxItems/maxAge branch.","commonSituations":"SQLite database file locked by another crowdsec process or deleted mid-run; MySQL/Postgres down or credentials rotated; ctx canceled because the machine is shutting down during flush.","solutions":["Check database connectivity and that the DB file/server is reachable and writable","Check crowdsec logs immediately above this error for the underlying ent/SQL driver message","If SQLite, ensure no other process holds the DB and the file is not corrupted (run `sqlite3 crowdsec.db 'PRAGMA integrity_check'`)","Retry after transient outage; flush will resume on next cycle"],"exampleFix":"// before (host down)\nif err != nil { return fmt.Errorf(\"could not get last alert: %w\", err) }\n// after: verify DB reachability before flush\nif err := c.Ent.Alert.Query().Limit(1).StringP(alert.FieldID); err != nil { log.Warn(\"db unreachable, skipping flush\"); return nil }","handlingStrategy":"try-catch","validationCode":"// ensure DB reachable before flush\nif err := c.Ent.Alert.Query().Limit(1).Select(alert.FieldID).String(ctx); err != nil {\n    // skip this flush cycle\n}","typeGuard":null,"tryCatchPattern":"err := c.FlushAlerts(ctx, since, maxItems)\nif err != nil && strings.Contains(err.Error(), \"could not get last alert\") {\n    log.Warnf(\"flush skipped, db read failed: %v\", err)\n}","preventionTips":["Keep the DB file/server reachable and monitored","Avoid killing crowdsec during flush cycles","On SQLite, prevent concurrent access by other tools"],"tags":["database","ent","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-14T00:17:10.932Z"}