crowdsecurity/crowdsec · error

unable to expire decisions %+v: %w

Error message

unable to expire decisions %+v: %w

What it means

After parsing UUIDs from a delete-decisions PAPI message, DecisionCmd expires the matching decisions in the local database via ExpireDecisionsWithFilter. This error means the database expiry operation failed, so remote deletion orders could not be applied locally.

Source

Thrown at pkg/apiserver/papi_cmd.go:63

		UUIDs := make([]string, 0)
		deleteDecisionMsg := deleteDecisions{
			Decisions: make([]string, 0),
		}

		if err := json.Unmarshal(data, &deleteDecisionMsg); err != nil {
			return fmt.Errorf("message for '%s' contains bad data format: %w", message.Header.OperationType, err)
		}

		UUIDs = append(UUIDs, deleteDecisionMsg.Decisions...)
		log.Infof("Decisions UUIDs to remove: %+v", UUIDs)

		filter := make(map[string][]string)
		filter["uuid"] = UUIDs

		_, deletedDecisions, err := p.DBClient.ExpireDecisionsWithFilter(ctx, filter)
		if err != nil {
			return fmt.Errorf("unable to expire decisions %+v: %w", UUIDs, err)
		}

		decisions := make([]*models.Decision, 0)

		for _, deletedDecision := range deletedDecisions {
			log.Infof("Decision from '%s' for '%s' (%s) has been deleted", deletedDecision.Origin, deletedDecision.Value, deletedDecision.Type)
			dec := &models.Decision{
				UUID:     deletedDecision.UUID,
				Origin:   &deletedDecision.Origin,
				Scenario: &deletedDecision.Scenario,
				Scope:    &deletedDecision.Scope,
				Value:    &deletedDecision.Value,
				ID:       int64(deletedDecision.ID),
				Until:    deletedDecision.Until.String(),
				Type:     &deletedDecision.Type,
			}
			decisions = append(decisions, dec)
		}

View on GitHub (pinned to 909b515798)

Solutions

  1. Check database connectivity and logs for the underlying wrapped SQL error
  2. Verify the database schema is up to date (cscli migrations / version match)
  3. Retry the pull once the DB is healthy
  4. Check disk space and DB lock contention
Defensive patterns

Strategy: retry

Validate before calling

if err := p.DBClient.Ping(ctx); err != nil { return fmt.Errorf("database unavailable before expiring decisions") }

Try / catch

if err := DecisionCmd(ctx, msg, p, false); err != nil { if strings.Contains(err.Error(), "unable to expire decisions") { /* retry with backoff; check DB health */ } }

Prevention

When it happens

Trigger: p.DBClient.ExpireDecisionsWithFilter returns an error — DB connectivity failure, SQL error, lock timeout, or an unsupported database backend.

Common situations: Database down or restarting (e.g. in a cluster), disk full, DB migration pending, SQLite lock contention under heavy load.

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


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/035372254796d1e0. Report an issue: GitHub.