{"record":{"id":"924ec40ef78e32b1","repo":"crowdsecurity/crowdsec","slug":"hard-delete-decisions-with-provided-filter-w","errorCode":null,"errorMessage":"hard delete decisions with provided filter: %w","messagePattern":"hard delete decisions with provided filter: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/decisions.go","lineNumber":336,"sourceCode":"\t\t\treturn err\n\t\t}\n\t\ttotal += rows\n\t\treturn nil\n\t})\n\n\treturn total, err\n}\n\n// deleteDecisionBatch removes the decisions as a single operation.\nfunc (c *Client) deleteDecisionBatch(ctx context.Context, batch []*ent.Decision) (int, error) {\n\tids := decisionIDs(batch)\n\n\trows, err := c.Ent.Decision.\n\t\tDelete().\n\t\tWhere(decision.IDIn(ids...)).\n\t\tExec(ctx)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"hard delete decisions with provided filter: %w\", err)\n\t}\n\n\treturn rows, nil\n}\n\n// DeleteDecisions removes a list of decisions from the database,\n// in multiple operations if len(decisions) > decisionDeleteBulkSize.\n// It returns the number of impacted decisions for the CAPI/PAPI, even in case of error.\nfunc (c *Client) DeleteDecisions(ctx context.Context, decisions []*ent.Decision) (int, error) {\n\tif len(decisions) == 0 {\n\t\treturn 0, nil\n\t}\n\n\ttotal := 0\n\terr := slicetools.Batch(ctx, decisions, decisionDeleteBulkSize, func(ctx context.Context, batch []*ent.Decision) error {\n\t\trows, err := c.deleteDecisionBatch(ctx, batch)\n\t\tif err != nil {\n\t\t\treturn err","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/decisions.go#L318-L354","documentation":"deleteDecisionBatch is the batched worker used by DeleteDecisions for hard deletions: it runs an ent Delete().Where(IDIn(ids...)).Exec(ctx). If the DELETE statement fails, the raw DB error is wrapped with this message, meaning rows could not be removed from the database.","triggerScenarios":"Ent Delete() failing for a batch of decision IDs: DB unreachable, foreign-key or trigger errors, context cancelled, lock contention with concurrent writers.","commonSituations":"cscli decisions delete --all racing with an active import; SQLite locked by another process; DB read-only (permissions) or disk full.","solutions":["Verify the DB is writable by the crowdsec user and not locked by another process","Retry; batches are independent so already-deleted rows are simply gone","Reduce batch size to shorten each transaction and lower contention","Check the wrapped inner error for the exact SQL/driver failure"],"exampleFix":"// before\n// running: crowdsec with a read-only sqlite file\n// after\n# chown crowdsec:crowdsec /var/lib/crowdsec/data/crowdsec.db && chmod u+w /var/lib/crowdsec/data/crowdsec.db","handlingStrategy":"retry","validationCode":"if len(ids) == 0 { return nil }\nif !dbWritable() { return errors.New(\"database is read-only\") }","typeGuard":null,"tryCatchPattern":"if _, err := client.DeleteDecisions(ctx, decisions, hardDelete); err != nil {\n    if strings.Contains(err.Error(), \"hard delete decisions with provided filter\") {\n        // inspect wrapped DB error; retry after checking locks/permissions\n    }\n    return err\n}","preventionTips":["Verify the crowdsec process owns and can write the DB file","Avoid deleting while bulk imports run to reduce lock contention","Use modest batch sizes for hard deletes"],"tags":["go","database","delete","ent"],"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-14T05:17:10.506Z"}