{"record":{"id":"4d05a0fbfc624d3b","repo":"crowdsecurity/crowdsec","slug":"invalid-contains-value-w-w","errorCode":null,"errorMessage":"invalid contains value: %w: %w","messagePattern":"invalid contains value: %w: %w","errorType":"exception","errorClass":"InvalidFilter","httpStatus":null,"severity":"error","filePath":"pkg/database/alertfilter.go","lineNumber":202,"sourceCode":"\t\terr               error\n\t\thasActiveDecision bool\n\t\trng               csnet.Range\n\t)\n\n\tcontains := true\n\n\t// if contains is true, return bans that *contains* the given value (value is the inner)\n\t// else, return bans that are *contained* by the given value (value is the outer)\n\n\thandleSimulatedFilter(filter, &predicates)\n\thandleOriginFilter(filter)\n\n\tfor param, value := range filter {\n\t\tswitch param {\n\t\tcase \"contains\":\n\t\t\tcontains, err = strconv.ParseBool(value[0])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid contains value: %w: %w\", err, InvalidFilter)\n\t\t\t}\n\t\tcase \"scope\":\n\t\t\thandleScopeFilter(value[0], &predicates)\n\t\tcase \"value\":\n\t\t\tpredicates = append(predicates, alert.SourceValueEQ(value[0]))\n\t\tcase \"scenario\":\n\t\t\tpredicates = append(predicates, alert.Or(\n\t\t\t\talert.ScenarioEQ(value[0]), // match alerts with no decisions\n\t\t\t\talert.HasDecisionsWith(decision.ScenarioEQ(value[0])),\n\t\t\t))\n\t\tcase \"ip\", \"range\":\n\t\t\trng, err = csnet.NewRange(value[0])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase \"since\", \"created_before\", \"until\":\n\t\t\tif err := handleTimeFilters(param, value[0], &predicates); err != nil {\n\t\t\t\treturn nil, err","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/alertfilter.go#L184-L220","documentation":"Returned by alertPredicatesFromFilter when the 'contains' query parameter cannot be parsed as a boolean via strconv.ParseBool. The parse error is wrapped together with the InvalidFilter sentinel, marking the request filter as invalid.","triggerScenarios":"A LAPI alerts query (applyAlertFilter or DeleteAlertWithFilter) passes contains=<not-bool>, e.g. contains=yes, contains=1.0, or contains= (empty).","commonSituations":"Hand-written curl calls using 'true/false' variants outside strconv's accepted set (1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False), or UI clients sending localized booleans.","solutions":["Use only strconv-accepted booleans: true/false, 1/0, t/f, TRUE/FALSE etc. (e.g. ?contains=true).","Fix the calling script/integration to emit lowercase true/false.","Validate client-side before sending: strconv.ParseBool(value) in Go or equivalent.","If a bouncer/integration sends this, update it — 'yes'/'no' are not accepted."],"exampleFix":"// before\nGET /v1/alerts?contains=yes\n// after\nGET /v1/alerts?contains=true","handlingStrategy":"validation","validationCode":"if _, err := strconv.ParseBool(containsVal); err != nil {\n    return fmt.Errorf(\"contains must be a Go boolean (true/false/1/0), got %q\", containsVal)\n}","typeGuard":null,"tryCatchPattern":"_, err := client.Alerts.List(ctx, models.GetAlertsOpts{Contains: &contains})\nvar apiErr *crowdsec.ApiErrorResponse\nif err != nil && errors.As(err, &apiErr) && apiErr.Message != nil {\n    // 400: fix the query parameter and retry once with corrected value\n}","preventionTips":["Serialize booleans as lowercase true/false in API clients","Never send 'yes'/'no'/'on'/'off' to crowdsec LAPI","Add query-param schema validation in API wrappers","Test integrations against a local LAPI instance"],"tags":["go","lapi","query-params"],"backgroundTag":"invalid-query-parameter","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}