crowdsecurity/crowdsec · warning · InvalidIPOrRange

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

Error message

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

What it means

applyDecisionFilter fails when the 'ip' or 'range' query parameter cannot be parsed into a network range by csnet.NewRange. The message's 'to int' wording is legacy/misleading; the real issue is an invalid IP address or CIDR value, wrapped with sentinel InvalidIPOrRange.

Source

Thrown at pkg/database/decisionfilter.go:80

			query = query.Where(decision.TypeEQ(value[0]))
		case "origins":
			query = query.Where(
				decision.OriginIn(strings.Split(value[0], ",")...),
			)
		case "scenarios_containing":
			predicates := decisionPredicatesFromStr(value[0], decision.ScenarioContainsFold)
			query = query.Where(decision.Or(predicates...))
		case "scenarios_not_containing":
			predicates := decisionPredicatesFromStr(value[0], decision.ScenarioContainsFold)
			query = query.Where(decision.Not(
				decision.Or(
					predicates...,
				),
			))
		case "ip", "range":
			rng, err = csnet.NewRange(value[0])
			if err != nil {
				return nil, fmt.Errorf("unable to convert '%s' to int: %w: %w", value[0], err, InvalidIPOrRange)
			}
		case "limit":
			limit, err := strconv.Atoi(value[0])
			if err != nil {
				return nil, fmt.Errorf("invalid limit value: %w: %w", err, InvalidFilter)
			}

			query = query.Limit(limit)
		case "offset":
			offset, err := strconv.Atoi(value[0])
			if err != nil {
				return nil, fmt.Errorf("invalid offset value: %w: %w", err, InvalidFilter)
			}

			query = query.Offset(offset)
		case "id_gt":
			id, err := strconv.Atoi(value[0])
			if err != nil {

View on GitHub (pinned to 909b515798)

Solutions

  1. Validate the value is a valid IP or CIDR (net.ParseIP / net.ParseCIDR) before sending.
  2. Use full CIDR form for ranges: 10.0.0.0/24, not 10.0.0/24.
  3. Remove the ip/range parameter to query without an IP filter.
  4. For IPv6, ensure correct compressed notation (e.g. 2001:db8::/32).

Example fix

// before
GET /v1/decisions?range=192.168.1.0/33
// after
GET /v1/decisions?range=192.168.1.0/24
Defensive patterns

Strategy: validation

Validate before calling

func validIPOrRange(v string) bool {
    if v == "" { return false }
    if strings.Contains(v, "/") {
        _, _, err := net.ParseCIDR(v)
        return err == nil
    }
    return net.ParseIP(v) != nil
}

Try / catch

if !validIPOrRange(ipParam) {
    return fmt.Errorf("refusing LAPI call: %q is not an IP or CIDR", ipParam)
}
resp, err := lapi.GetDecisions(ctx, models.GetDecisionsOpts{IP: &ipParam})
if err != nil && strings.Contains(err.Error(), "InvalidIPOrRange") {
    return fmt.Errorf("bad ip/range %q: %w", ipParam, err)
}

Prevention

When it happens

Trigger: Calling LAPI decision endpoints with ?ip=not-an-ip, ?range=10.0.0/24 (bad CIDR), ?ip=999.1.1.1, or an IPv6 with wrong syntax.

Common situations: Hand-built curl commands with typos in CIDR notation; scripts interpolating empty strings for ip/range; mixing prefix lengths wrong (e.g. /33); passing a hostname instead of an IP.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


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