crowdsecurity/crowdsec · error · InvalidIPOrRange

unable to convert '%s' to int: %w: %w

Error message

unable to convert '%s' to int: %w: %w

What it means

When the filter contains the 'ip' or 'range' key, ExpireDecisionsWithFilter parses the value into a csnet.Range via csnet.NewRange. On parse failure the error is wrapped with a misleading historical message ('unable to convert to int') plus the InvalidIPOrRange sentinel; it really means the value is not a valid IP address or CIDR range.

Source

Thrown at pkg/database/decisions.go:251

		case "contains":
			contains, err = strconv.ParseBool(value[0])
			if err != nil {
				return 0, nil, fmt.Errorf("invalid contains value: %w: %w", err, InvalidFilter)
			}
		case "scopes":
			decisions = decisions.Where(decision.ScopeEQ(value[0]))
		case "uuid":
			decisions = decisions.Where(decision.UUIDIn(value...))
		case "origin":
			decisions = decisions.Where(decision.OriginEQ(value[0]))
		case "value":
			decisions = decisions.Where(decision.ValueEQ(value[0]))
		case "type":
			decisions = decisions.Where(decision.TypeEQ(value[0]))
		case "ip", "range":
			rng, err = csnet.NewRange(value[0])
			if err != nil {
				return 0, nil, fmt.Errorf("unable to convert '%s' to int: %w: %w", value[0], err, InvalidIPOrRange)
			}
		case "scenario":
			decisions = decisions.Where(decision.ScenarioEQ(value[0]))
		default:
			return 0, nil, fmt.Errorf("'%s' doesn't exist: %w", param, InvalidFilter)
		}
	}

	decisions, err = decisionIPFilter(decisions, contains, rng)
	if err != nil {
		return 0, nil, err
	}

	decisionsToDelete, err := decisions.All(ctx)
	if err != nil {
		c.Log.Warningf("ExpireDecisionsWithFilter : %s", err)
		return 0, nil, fmt.Errorf("expire decisions with provided filter: %w", DeleteFail)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Validate the value is a correct IP or CIDR (net.ParseIP / net.ParseCIDR) before passing it
  2. Correct the typo in the ip/range filter value (e.g. 192.168.1.0/24, not 192.168.1.300)
  3. Check errors.Is(err, database.InvalidIPOrRange) to distinguish this from other expiry failures

Example fix

// before
c.ExpireDecisionsWithFilter(ctx, map[string][]string{"range": {"10.0.0/24"}})
// after
if _, _, err := net.ParseCIDR("10.0.0.0/24"); err == nil {
    c.ExpireDecisionsWithFilter(ctx, map[string][]string{"range": {"10.0.0.0/24"}})
}
Defensive patterns

Strategy: validation

Validate before calling

func validIPOrRange(v string) bool {
    if net.ParseIP(v) != nil { return true }
    _, _, err := net.ParseCIDR(v)
    return err == nil
}

Try / catch

err := errors.Is(err, database.InvalidIPOrRange) // classify before showing to user
if errors.Is(err, database.InvalidIPOrRange) {
    return status.Errorf(codes.InvalidArgument, "invalid ip/range filter: %v", err)
}

Prevention

When it happens

Trigger: Calling ExpireDecisionsWithFilter with filter keys 'ip' or 'range' whose value is not a valid IP/CIDR (e.g. "192.168.1.300", "10.0.0/24", or a hostname).

Common situations: Hand-typed CLI arguments with typos in the CIDR; copy-pasted values retaining prefixes like 'ip:1.2.3.4'; scripts building ranges from unvalidated user input.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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