crowdsecurity/crowdsec · error
unable to convert '%s' to int: %w
Error message
unable to convert '%s' to int: %w
What it means
CountDecisionsByValue parses its value argument into a csnet.Range with csnet.NewRange before counting matching decisions. If the value is not a valid IP address or CIDR range, NewRange fails and the error is wrapped (the 'convert to int' wording is legacy; it means an invalid IP/range value).
Source
Thrown at pkg/database/decisions.go:385
// XXX: do we want 500 or 404 here?
if err != nil || len(toUpdate) == 0 {
c.Log.Warningf("ExpireDecisionByID : %v (nb expired: %d)", err, len(toUpdate))
return 0, nil, fmt.Errorf("decision with id '%d' doesn't exist: %w", decisionID, DeleteFail)
}
if len(toUpdate) == 0 {
return 0, nil, ItemNotFound
}
count, err := c.ExpireDecisions(ctx, toUpdate)
return count, toUpdate, err
}
func (c *Client) CountDecisionsByValue(ctx context.Context, value string, since *time.Time, onlyActive bool) (int, error) {
rng, err := csnet.NewRange(value)
if err != nil {
return 0, fmt.Errorf("unable to convert '%s' to int: %w", value, err)
}
contains := true
decisions := c.Ent.Decision.Query()
decisions, err = decisionIPFilter(decisions, contains, rng)
if err != nil {
return 0, fmt.Errorf("fail to apply StartIpEndIpFilter: %w", err)
}
if since != nil {
decisions = decisions.Where(decision.CreatedAtGT(*since))
}
if onlyActive {
decisions = decisions.Where(decision.UntilGT(time.Now().UTC()))
}
View on GitHub (pinned to 909b515798)
Solutions
- Validate the value with net.ParseIP or net.ParseCIDR before calling
- Ensure the value is a plain IP (1.2.3.4) or full CIDR (1.2.3.0/24), with no extra prefixes or whitespace
- Return a 400 to upstream callers when the wrapped error surfaces instead of retrying
Example fix
// before
count, err := client.CountDecisionsByValue(ctx, r.URL.Query().Get("value"), nil, true)
// after
v := strings.TrimSpace(r.URL.Query().Get("value"))
if net.ParseIP(v) == nil {
if _, _, cidrErr := net.ParseCIDR(v); cidrErr != nil {
http.Error(w, "value must be an IP or CIDR", http.StatusBadRequest)
return
}
}
count, err := client.CountDecisionsByValue(ctx, v, nil, true) 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
}
if !validIPOrRange(value) { return ErrInvalidValue } Try / catch
if _, err := client.CountDecisionsByValue(ctx, value, since, onlyActive); err != nil {
if strings.Contains(err.Error(), "unable to convert") {
return status.Error(codes.InvalidArgument, "value must be an IP or CIDR")
}
return err
} Prevention
- Validate with net.ParseIP/net.ParseCIDR before calling
- Trim and normalize input; reject empty strings early
- Only accept literal IPs/CIDRs — no hostnames or 'ip:' prefixes
When it happens
Trigger: Calling CountDecisionsByValue with value="bad-input", an empty string, a hostname, or a malformed CIDR like "1.2.3/32".
Common situations: Bouncer/API callers forwarding raw user input as the value; empty query parameters; IPv6 written with typos; values missing their /prefix for ranges.
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
- unable to convert '%s' to int: %w: %w
- invalid CIDR range '%s' for bot entry '%s' in %s: %w
- invalid ip range '%s': %w
- missing allowed_ranges value for api.server.auto_register
- out of bound gid
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/35ee0e7c7a1a9922.
Report an issue: GitHub.