{"record":{"id":"60fce9eac94596bb","repo":"crowdsecurity/crowdsec","slug":"groupby-wrong-type","errorCode":null,"errorMessage":"groupby wrong type","messagePattern":"groupby wrong type","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/leakybucket/manager_run.go","lineNumber":255,"sourceCode":"\t\t\t}\n\t\t\tif !condition {\n\t\t\t\tholders[idx].logger.Debugf(\"Event leaving node : ko (filter mismatch)\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// groupby determines the partition key for the specific bucket\n\t\tvar groupby string\n\t\tif holders[idx].RunTimeGroupBy != nil {\n\t\t\ttmpGroupBy, err := exprhelpers.Run(holders[idx].RunTimeGroupBy, map[string]any{\"evt\": &parsed}, holders[idx].logger, holders[idx].Spec.Debug)\n\t\t\tif err != nil {\n\t\t\t\tholders[idx].logger.Errorf(\"failed groupby : %v\", err)\n\t\t\t\treturn false, errors.New(\"leaky failed :/\")\n\t\t\t}\n\n\t\t\tif groupby, ok = tmpGroupBy.(string); !ok {\n\t\t\t\tholders[idx].logger.Fatalf(\"failed groupby type : %v\", err)\n\t\t\t\treturn false, errors.New(\"groupby wrong type\")\n\t\t\t}\n\t\t}\n\t\tbuckey := holders[idx].BucketKey(groupby)\n\n\t\t// we need to either find the existing bucket, or create a new one (if it's the first event to hit it for this partition key)\n\t\tbucket, err := LoadOrStoreBucketFromHolder(ctx, buckey, buckets, &holders[idx], parsed.ExpectMode)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"failed to load or store bucket: %w\", err)\n\t\t}\n\t\t// finally, pour the even into the bucket\n\n\t\tif bucket.Factory.orderEvent {\n\t\t\tif orderEvent == nil {\n\t\t\t\torderEvent = make(map[string]*sync.WaitGroup)\n\t\t\t}\n\t\t\tif orderEvent[buckey] != nil {\n\t\t\t\torderEvent[buckey].Wait()\n\t\t\t} else {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/leakybucket/manager_run.go#L237-L273","documentation":"After running the groupby expression, the result must be a string to use as the bucket partition key. If the expr returns a non-string (number, bool, array), PourItemToHolders logs a fatal and returns this error instead of building a key.","triggerScenarios":"A scenario's `groupby:` expression evaluates successfully but returns a non-string value, e.g. `groupby: \"evt.Parsed.status\"` where status is an int.","commonSituations":"Groupby on numeric or boolean parsed fields, or on lists/maps, when the author intended a string identifier.","solutions":["Cast the groupby expression to a string, e.g. `groupby: \"string(evt.Parsed.status)\"` or wrap with fmt","Group on inherently string fields like evt.Meta.source_ip or evt.Parsed.username","Restart crowdsec if it hit the Fatalf path, as the process may have exited"],"exampleFix":"# before\ngroupby: \"evt.Parsed.status_code\"\n# after\ngroupby: \"fmt.Sprintf(\"%v\", evt.Parsed.status_code)\"","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"if s, ok := groupbyResult.(string); ok {\n    key = s\n} else {\n    return fmt.Errorf(\"groupby must return string, got %T\", groupbyResult)\n}","tryCatchPattern":null,"preventionTips":["Wrap non-string groupby values: fmt.Sprintf(\"%v\", ...)","Prefer string-typed meta fields for groupby","Beware numeric Parsed fields — expressions may yield floats"],"tags":["crowdsec","leakybucket","expr","type-mismatch"],"backgroundTag":"type-mismatch","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"}