{"record":{"id":"74bc2afb68dfe006","repo":"crowdsecurity/crowdsec","slug":"expire-decisions-with-provided-filter-w","errorCode":null,"errorMessage":"expire decisions with provided filter: %w","messagePattern":"expire decisions with provided filter: %w","errorType":"exception","errorClass":"DeleteFail","httpStatus":null,"severity":"error","filePath":"pkg/database/decisions.go","lineNumber":268,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn 0, nil, fmt.Errorf(\"unable to convert '%s' to int: %w: %w\", value[0], err, InvalidIPOrRange)\n\t\t\t}\n\t\tcase \"scenario\":\n\t\t\tdecisions = decisions.Where(decision.ScenarioEQ(value[0]))\n\t\tdefault:\n\t\t\treturn 0, nil, fmt.Errorf(\"'%s' doesn't exist: %w\", param, InvalidFilter)\n\t\t}\n\t}\n\n\tdecisions, err = decisionIPFilter(decisions, contains, rng)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tdecisionsToDelete, err := decisions.All(ctx)\n\tif err != nil {\n\t\tc.Log.Warningf(\"ExpireDecisionsWithFilter : %s\", err)\n\t\treturn 0, nil, fmt.Errorf(\"expire decisions with provided filter: %w\", DeleteFail)\n\t}\n\n\tcount, err := c.ExpireDecisions(ctx, decisionsToDelete)\n\tif err != nil {\n\t\treturn 0, nil, fmt.Errorf(\"expire decisions with provided filter: %w: %w\", err, DeleteFail)\n\t}\n\n\treturn count, decisionsToDelete, err\n}\n\nfunc decisionIDs(decisions []*ent.Decision) []int {\n\tids := make([]int, len(decisions))\n\tfor i, d := range decisions {\n\t\tids[i] = d.ID\n\t}\n\n\treturn ids\n}","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/decisions.go#L250-L286","documentation":"After building the query, ExpireDecisionsWithFilter fetches the matching decisions with decisions.All(ctx). If the database query fails (connection issue, schema problem, context cancellation), it logs a warning and wraps the failure in the DeleteFail sentinel with this message, aborting the expiry operation.","triggerScenarios":"decisions.All(ctx) returning a DB error: database unreachable, ent schema mismatch, context deadline exceeded during a large filtered query.","commonSituations":"SQLite file locked or corrupted; LAPI losing the DB connection mid-request; very broad filters scanning millions of rows and timing out; migrations not applied after an upgrade.","solutions":["Check database connectivity and that ent migrations ran (cscli db migrate / restart with a healthy DB)","Narrow the filter so the query returns fewer rows and completes before context timeout","Inspect the logged warning 'ExpireDecisionsWithFilter' for the underlying DB error","Check errors.Is(err, database.DeleteFail) in callers to map to a 500"],"exampleFix":"// before\nres, err := client.ExpireDecisionsWithFilter(ctx, filter) // ctx without deadline\n// after\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nres, err := client.ExpireDecisionsWithFilter(ctx, filter)","handlingStrategy":"retry","validationCode":"if err := entClient.Decision.Query().Limit(1).FirstErr(ctx); err != nil {\n    return fmt.Errorf(\"database unavailable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"count, _, err := client.ExpireDecisionsWithFilter(ctx, filter)\nif err != nil {\n    if errors.Is(err, database.DeleteFail) && netErrTransient(err) {\n        time.Sleep(backoff); return client.ExpireDecisionsWithFilter(ctx, filter)\n    }\n    return err\n}","preventionTips":["Set a generous context timeout for bulk filtered queries","Keep DB migrations current before running expiry jobs","Monitor the 'ExpireDecisionsWithFilter' warning log for early DB degradation"],"tags":["go","database","query","ent"],"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"}