{"id":"069468ab5881aeb8","repo":"go-redis/redis","slug":"redis-ft-aggregate-collect-empty-field-name-in-f","errorCode":null,"errorMessage":"redis: FT.AGGREGATE COLLECT: empty field name in Fields","messagePattern":"redis: FT\\.AGGREGATE COLLECT: empty field name in Fields","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_collect.go","lineNumber":94,"sourceCode":"\treturn \"@\" + strings.TrimLeft(name, \"@\")\n}\n\n// buildCollectArgs renders a FTAggregateCollect into the reducer argument\n// token list (everything after \"REDUCE COLLECT <narg>\", excluding AS <alias>).\n// The serializer computes <narg> as len(args), which matches the COLLECT\n// contract: narg counts every FIELDS/DISTINCT/SORTBY/LIMIT token.\nfunc buildCollectArgs(o FTAggregateCollect) ([]interface{}, error) {\n\targs := make([]interface{}, 0, 8)\n\n\t// FIELDS (required): either * or a counted list of @-names.\n\tswitch {\n\tcase o.FieldsAll:\n\t\targs = append(args, \"FIELDS\", \"*\")\n\tcase len(o.Fields) > 0:\n\t\targs = append(args, \"FIELDS\", len(o.Fields))\n\t\tfor _, f := range o.Fields {\n\t\t\tif strings.TrimLeft(f, \"@\") == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"redis: FT.AGGREGATE COLLECT: empty field name in Fields\")\n\t\t\t}\n\t\t\targs = append(args, ensureAtPrefix(f))\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list\")\n\t}\n\n\t// DISTINCT (optional, forward-compatible).\n\tif o.Distinct {\n\t\targs = append(args, \"DISTINCT\")\n\t}\n\n\t// SORTBY (optional). sort_narg counts each field plus its optional\n\t// direction token.\n\tif len(o.SortBy) > 0 {\n\t\tsortTokens := make([]interface{}, 0, len(o.SortBy)*2)\n\t\tfor _, s := range o.SortBy {\n\t\t\tif strings.TrimLeft(s.FieldName, \"@\") == \"\" {","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_collect.go#L76-L112","documentation":"Returned from NewCollectReducer (and surfaced via Collect/Run) when a field name in FTAggregateCollect.Fields is empty after stripping leading '@' characters. COLLECT requires concrete field names to project; an empty/whitespace/'@' entry cannot be rendered as @<name>.","triggerScenarios":"Passing FTAggregateCollect{Fields: []string{\"\", \"@\"}} or any entry whose strings.TrimLeft(f, \"@\") is empty. The check fires at reducer construction (NewCollectReducer) or via AggregateBuilder.Collect.","commonSituations":"Field list built dynamically from struct tags/JSON that produced empty strings; a stray \"@\" placeholder; trailing empty element from a strings.Split on a trailing delimiter; copy-paste leaving an empty slot.","solutions":["Filter the Fields slice to drop entries that are empty or contain only '@' before constructing FTAggregateCollect.","Validate each field name with strings.TrimSpace(strings.TrimLeft(f, \"@\")) != \"\" before use.","Fix the upstream producer so it never yields empty field names."],"exampleFix":"// before\ncollect := redis.FTAggregateCollect{Fields: []string{\"@sku\", \"\", \"@price\"}}\nb.GroupBy(\"@order\").Collect(collect)\n\n// after\nfields := []string{\"@sku\", \"@price\"}\ncollect := redis.FTAggregateCollect{Fields: fields}\nb.GroupBy(\"@order\").Collect(collect)","handlingStrategy":"validation","validationCode":"for _, f := range collect.Fields {\n    if strings.TrimSpace(strings.TrimLeft(f, \"@\")) == \"\" {\n        return fmt.Errorf(\"empty field name in COLLECT.Fields: %q\", f)\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := redis.NewCollectReducer(collect); err != nil {\n    if strings.Contains(err.Error(), \"empty field name\") {\n        // sanitize the Fields slice and retry\n    }\n}","preventionTips":["Sanitize dynamic field lists at the boundary where they are produced.","Default to FieldsAll=true when the field set is not known in advance.","Add a unit test that builds FTAggregateCollect from representative inputs."],"tags":["ftaggregate","search","collect","validation"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}