go-redis/redis · error

redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empt

Error message

redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list

What it means

Returned from NewCollectReducer when FTAggregateCollect has neither FieldsAll=true nor a non-empty Fields slice. COLLECT must project something (either FIELDS * or an explicit field list); an empty projection is rejected at construction time before any command is sent.

Source

Thrown at search_collect.go:99

// The serializer computes <narg> as len(args), which matches the COLLECT
// contract: narg counts every FIELDS/DISTINCT/SORTBY/LIMIT token.
func buildCollectArgs(o FTAggregateCollect) ([]interface{}, error) {
	args := make([]interface{}, 0, 8)

	// FIELDS (required): either * or a counted list of @-names.
	switch {
	case o.FieldsAll:
		args = append(args, "FIELDS", "*")
	case len(o.Fields) > 0:
		args = append(args, "FIELDS", len(o.Fields))
		for _, f := range o.Fields {
			if strings.TrimLeft(f, "@") == "" {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: empty field name in Fields")
			}
			args = append(args, ensureAtPrefix(f))
		}
	default:
		return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list")
	}

	// DISTINCT (optional, forward-compatible).
	if o.Distinct {
		args = append(args, "DISTINCT")
	}

	// SORTBY (optional). sort_narg counts each field plus its optional
	// direction token.
	if len(o.SortBy) > 0 {
		sortTokens := make([]interface{}, 0, len(o.SortBy)*2)
		for _, s := range o.SortBy {
			if strings.TrimLeft(s.FieldName, "@") == "" {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: empty field name in SortBy")
			}
			if s.Asc && s.Desc {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclusive")
			}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Set FieldsAll=true if you want all pipeline fields projected (FIELDS *), or populate Fields with at least one name.
  2. Add a validation check at construction: require either FieldsAll || len(Fields) > 0.
  3. If unsure which fields exist, use FieldsAll=true (optionally paired with an upstream LOAD *).

Example fix

// before
collect := redis.FTAggregateCollect{} // no FieldsAll, no Fields
b.GroupBy("@order").Collect(collect)

// after
collect := redis.FTAggregateCollect{FieldsAll: true}
b.GroupBy("@order").Collect(collect)
Defensive patterns

Strategy: validation

Validate before calling

if !collect.FieldsAll && len(collect.Fields) == 0 {
    return errors.New("COLLECT requires FieldsAll or a non-empty Fields list")
}

Try / catch

if _, err := redis.NewCollectReducer(collect); err != nil {
    if strings.Contains(err.Error(), "requires FieldsAll or a non-empty Fields") {
        collect.FieldsAll = true // or set Fields explicitly
    }
}

Prevention

When it happens

Trigger: Passing FTAggregateCollect{} (zero value) or FTAggregateCollect{Fields: nil} to AggregateBuilder.Collect or NewCollectReducer. FieldsAll defaults to false and Fields defaults to nil, so the default branch is an error.

Common situations: Building FTAggregateCollect conditionally and forgetting to set either FieldsAll or Fields; refactoring that removed the Fields assignment; assuming COLLECT defaults to FIELDS * (it does not).

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/a76cebf1600aa2a8.json. Report an issue: GitHub.