{"record":{"id":"35ee0e7c7a1a9922","repo":"crowdsecurity/crowdsec","slug":"unable-to-convert-s-to-int-w","errorCode":null,"errorMessage":"unable to convert '%s' to int: %w","messagePattern":"unable to convert '(.+?)' to int: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/decisions.go","lineNumber":385,"sourceCode":"\t// XXX: do we want 500 or 404 here?\n\tif err != nil || len(toUpdate) == 0 {\n\t\tc.Log.Warningf(\"ExpireDecisionByID : %v (nb expired: %d)\", err, len(toUpdate))\n\t\treturn 0, nil, fmt.Errorf(\"decision with id '%d' doesn't exist: %w\", decisionID, DeleteFail)\n\t}\n\n\tif len(toUpdate) == 0 {\n\t\treturn 0, nil, ItemNotFound\n\t}\n\n\tcount, err := c.ExpireDecisions(ctx, toUpdate)\n\n\treturn count, toUpdate, err\n}\n\nfunc (c *Client) CountDecisionsByValue(ctx context.Context, value string, since *time.Time, onlyActive bool) (int, error) {\n\trng, err := csnet.NewRange(value)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"unable to convert '%s' to int: %w\", value, err)\n\t}\n\n\tcontains := true\n\tdecisions := c.Ent.Decision.Query()\n\n\tdecisions, err = decisionIPFilter(decisions, contains, rng)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"fail to apply StartIpEndIpFilter: %w\", err)\n\t}\n\n\tif since != nil {\n\t\tdecisions = decisions.Where(decision.CreatedAtGT(*since))\n\t}\n\n\tif onlyActive {\n\t\tdecisions = decisions.Where(decision.UntilGT(time.Now().UTC()))\n\t}\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/decisions.go#L367-L403","documentation":"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).","triggerScenarios":"Calling CountDecisionsByValue with value=\"bad-input\", an empty string, a hostname, or a malformed CIDR like \"1.2.3/32\".","commonSituations":"Bouncer/API callers forwarding raw user input as the value; empty query parameters; IPv6 written with typos; values missing their /prefix for ranges.","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"],"exampleFix":"// before\ncount, err := client.CountDecisionsByValue(ctx, r.URL.Query().Get(\"value\"), nil, true)\n// after\nv := strings.TrimSpace(r.URL.Query().Get(\"value\"))\nif net.ParseIP(v) == nil {\n    if _, _, cidrErr := net.ParseCIDR(v); cidrErr != nil {\n        http.Error(w, \"value must be an IP or CIDR\", http.StatusBadRequest)\n        return\n    }\n}\ncount, err := client.CountDecisionsByValue(ctx, v, nil, true)","handlingStrategy":"validation","validationCode":"func validIPOrRange(v string) bool {\n    if net.ParseIP(v) != nil { return true }\n    _, _, err := net.ParseCIDR(v)\n    return err == nil\n}\nif !validIPOrRange(value) { return ErrInvalidValue }","typeGuard":null,"tryCatchPattern":"if _, err := client.CountDecisionsByValue(ctx, value, since, onlyActive); err != nil {\n    if strings.Contains(err.Error(), \"unable to convert\") {\n        return status.Error(codes.InvalidArgument, \"value must be an IP or CIDR\")\n    }\n    return err\n}","preventionTips":["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"],"tags":["go","network","cidr","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}