crowdsecurity/crowdsec · error
unable to expire decisions for list %s : %w
Error message
unable to expire decisions for list %s : %w
What it means
After a valid blocklist_unsubscribe command, ManagementCmd expires all decisions with origin=lists and scenario=<blocklist name> via DBClient.ExpireDecisionsWithFilter. This error wraps any failure returned by that database operation, typically an SQLite/DB connectivity or lock problem, so the unsubscribe does not silently leave stale decisions.
Source
Thrown at pkg/apiserver/papi_cmd.go:219
unsubscribeMsg := blocklistUnsubscribe{}
if err := json.Unmarshal(data, &unsubscribeMsg); err != nil {
return fmt.Errorf("message for '%s' contains bad data format: %w", message.Header.OperationType, err)
}
if unsubscribeMsg.Name == "" {
return fmt.Errorf("message for '%s' contains bad data format: missing blocklist name", message.Header.OperationType)
}
p.Logger.Infof("Received blocklist_unsubscribe command from PAPI, unsubscribing from blocklist %s", unsubscribeMsg.Name)
filter := make(map[string][]string)
filter["origin"] = []string{types.ListOrigin}
filter["scenario"] = []string{unsubscribeMsg.Name}
_, deletedDecisions, err := p.DBClient.ExpireDecisionsWithFilter(ctx, filter)
if err != nil {
return fmt.Errorf("unable to expire decisions for list %s : %w", unsubscribeMsg.Name, err)
}
p.Logger.Infof("deleted %d decisions for list %s", len(deletedDecisions), unsubscribeMsg.Name)
case "reauth":
p.Logger.Infof("Received reauth command from PAPI, resetting token")
p.apiClient.GetClient().Transport.(*apiclient.JWTTransport).ResetToken()
case "force_pull":
data, err := json.Marshal(message.Data)
if err != nil {
return err
}
forcePullMsg := forcePull{}
if err := json.Unmarshal(data, &forcePullMsg); err != nil {
return fmt.Errorf("message for '%s' contains bad data format: %w", message.Header.OperationType, err)
}
View on GitHub (pinned to 909b515798)
Solutions
- Read the wrapped cause at the end of the error message to identify the DB failure (e.g. 'database is locked')
- Stop competing crowdsec processes (cscli, other agents sharing the same data_dir) and retry
- Check free disk space and permissions on the SQLite database file (default /var/lib/crowdsec/data/crowdsec.db)
- If corruption is suspected, restore from backup or run sqlite3 .recover on the DB
Defensive patterns
Strategy: retry
Validate before calling
// verify DB is writable/healthy before processing commands
if _, err := os.Stat(dbPath); err != nil {
return fmt.Errorf("crowdsec database not found: %w", err)
} Try / catch
if err := ManagementCmd(ctx, msg, p, false); err != nil {
if strings.Contains(err.Error(), "unable to expire decisions") {
log.Errorf("DB failure during unsubscribe, will retry: %v", err)
return retryAfterBackoff(err)
}
return err
} Prevention
- Avoid running multiple crowdsec processes sharing one SQLite DB without a lock strategy
- Monitor disk space on the volume hosting crowdsec.db
- Schedule DB-heavy cscli operations off-peak to reduce lock contention
When it happens
Trigger: ExpireDecisionsWithFilter returns an error while deleting decisions for origin=types.ListOrigin and scenario=unsubscribeMsg.Name — e.g. database locked, disk full, or corrupted DB file.
Common situations: SQLite database locked by a concurrent LAPI writer; disk quota exceeded on the machine hosting the crowdsec DB; database file corruption after an unclean shutdown.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- %s: %w
- bulk creating alert: %w: %w
- committing alert transaction: %w: %w
- unable to add values to allowlist: %w
- creating machine '%s': %w
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/32dc42e56e16ffa9.
Report an issue: GitHub.