{"record":{"id":"46b488191fe311a9","repo":"redis/go-redis","slug":"redis-ft-aggregate-collect-requires-fieldsall-or","errorCode":null,"errorMessage":"redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list","messagePattern":"redis: FT\\.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_collect.go","lineNumber":99,"sourceCode":"// 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, \"@\") == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"redis: FT.AGGREGATE COLLECT: empty field name in SortBy\")\n\t\t\t}\n\t\t\tif s.Asc && s.Desc {\n\t\t\t\treturn nil, fmt.Errorf(\"redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclusive\")\n\t\t\t}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/search_collect.go#L81-L117","documentation":"buildCollectArgs requires exactly one of two field-selection modes for the COLLECT step: either FieldsAll (emitting FIELDS *) or a non-empty Fields list. If neither is set there is nothing to collect, so argument construction fails with this error.","triggerScenarios":"NewCollectReducer with CollectOptions where FieldsAll is false and Fields is empty or nil; buildCollectArgs falls to the default branch and returns this error.","commonSituations":"Constructing CollectOptions with a dynamically built Fields slice that ended up empty; forgetting to set FieldsAll when no explicit fields are wanted.","solutions":["Set FieldsAll: true to collect all fields","Populate Fields with at least one valid field name","Guard the construction site so an empty field list falls back to FieldsAll"],"exampleFix":"// before\nr := redis.NewCollectReducer(redis.CollectOptions{})\n// after\nr := redis.NewCollectReducer(redis.CollectOptions{FieldsAll: true})","handlingStrategy":"validation","validationCode":"if !opts.FieldsAll && len(opts.Fields) == 0 {\n    return fmt.Errorf(\"CollectOptions requires FieldsAll or a non-empty Fields list\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to FieldsAll:true when building options from config","Check len(Fields) before constructing CollectOptions","Centralize CollectOptions construction in one enforcing helper"],"tags":["search","ft-aggregate","collect","validation"],"backgroundTag":"missing-required-option","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}