crowdsecurity/crowdsec · error · QueryFail

expired decisions with filters: %w

Error message

expired decisions with filters: %w

What it means

Returned by QueryExpiredDecisionsSinceWithFilters when applyDecisionFilter cannot translate the filter map into ent predicates. Wraps QueryFail ("unable to query"); the parse error is logged as 'QueryExpiredDecisionsSinceWithFilters : <err>'.

Source

Thrown at pkg/database/decisions.go:205

	query := c.Ent.Decision.Query().
		Select(decision.FieldID, decision.FieldUntil, decision.FieldScenario, decision.FieldScope, decision.FieldValue, decision.FieldType, decision.FieldOrigin, decision.FieldUUID).
		Where(
			decision.UntilLT(now),
		)

	if since != nil {
		query = query.Where(decision.UntilGT(*since))
	}

	// Allow a bouncer to ask for non-deduplicated results
	if v, ok := filter["dedup"]; !ok || v[0] != "false" {
		query = query.Where(longestDecisionForScopeTypeValue)
	}

	query, err := applyDecisionFilter(query, filter)
	if err != nil {
		c.Log.Warningf("QueryExpiredDecisionsSinceWithFilters : %s", err)
		return []*ent.Decision{}, fmt.Errorf("expired decisions with filters: %w", QueryFail)
	}

	query = query.Order(ent.Asc(decision.FieldID))

	data, err := query.All(ctx)
	if err != nil {
		c.Log.Warningf("QueryExpiredDecisionsSinceWithFilters : %s", err)
		return []*ent.Decision{}, fmt.Errorf("expired decisions with filters: %w", QueryFail)
	}

	return data, nil
}

// ExpireDecisionsWithFilter updates the expiration time to now() for the decisions matching the filter, and returns the updated items
func (c *Client) ExpireDecisionsWithFilter(ctx context.Context, filter map[string][]string) (int, []*ent.Decision, error) {
	var (
		err error
		rng csnet.Range

View on GitHub (pinned to 909b515798)

Solutions

  1. Find the actual parse failure in the 'QueryExpiredDecisionsSinceWithFilters :' log line
  2. Correct the filter keys/values (check 'since' duration format and scope/value formats)
  3. URL-encode query parameters when calling the LAPI HTTP endpoint directly
  4. Align bouncer and LAPI versions if the filter vocabulary differs

Example fix

// before
GET /v1/decisions/stream?since=last-hour
// after
GET /v1/decisions/stream?since=1h
Defensive patterns

Strategy: validation

Validate before calling

const sinceRe = `^([0-9]+(ns|us|ms|s|m|h))+$`
if !regexp.MustCompile(sinceRe).MatchString(since) {
    return fmt.Errorf("invalid since %q; use Go duration format like 1h30m", since)
}

Try / catch

res, err := client.QueryExpiredDecisionsSinceWithFilters(ctx, since, filter)
if err != nil && strings.Contains(err.Error(), "unable to query") {
    return fmt.Errorf("filter rejected: %w (see LAPI log 'QueryExpiredDecisionsSinceWithFilters')", err)
}

Prevention

When it happens

Trigger: Caller (e.g. stream/ expired-decision pull with a 'since' parameter) passes an invalid filter value — unparsable duration for since, malformed scope/type/value, or bad IP in an ip filter.

Common situations: Custom bouncers or scripts calling LAPI /decisions/stream with an unencoded or malformed query string; version drift where a newer client sends filter keys the server's applyDecisionFilter rejects.

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/d9865c327ae18df1. Report an issue: GitHub.