{"id":"5fb8268cd988676b","repo":"go-redis/redis","slug":"redis-collect-value-has-type-t-want-array-of-en","errorCode":null,"errorMessage":"redis: COLLECT value has type %T, want array of entries","messagePattern":"redis: COLLECT value has type %T, want array of entries","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_collect.go","lineNumber":192,"sourceCode":"// preserved as returned by the server; it is meaningful only when the COLLECT\n// reducer was given a SORTBY.\nfunc (r AggregateRow) Collect(alias string) (CollectColumn, error) {\n\tv, ok := r.Fields[alias]\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\treturn parseCollectValue(v)\n}\n\n// parseCollectValue decodes a raw COLLECT alias value (an array of entries)\n// into a CollectColumn.\nfunc parseCollectValue(v interface{}) (CollectColumn, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\tarr, ok := v.([]interface{})\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"redis: COLLECT value has type %T, want array of entries\", v)\n\t}\n\tout := make(CollectColumn, 0, len(arr))\n\tfor i, e := range arr {\n\t\tentry, err := parseCollectEntry(e)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"redis: COLLECT entry %d: %w\", i, err)\n\t\t}\n\t\tout = append(out, entry)\n\t}\n\treturn out, nil\n}\n\n// parseCollectEntry decodes a single collected entry from either the RESP3\n// map form or the RESP2 flat key/value array form into a CollectEntry. Keys\n// are passed through as-is: the server already returns them without the \"@\"\n// prefix.\nfunc parseCollectEntry(e interface{}) (CollectEntry, error) {\n\tswitch m := e.(type) {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_collect.go#L174-L210","documentation":"Returned by AggregateRow.Collect (via parseCollectValue) when the value stored under the requested alias is present but is not a []interface{}. The COLLECT reducer column is expected to be an array of per-entry maps/arrays, so any other concrete Go type (string, int64, map, nil-nonzero) is treated as a malformed/foreign value. The '%T' slot is filled with the actual Go type to aid diagnosis.","triggerScenarios":"Calling row.Collect(\"myAlias\") when \"myAlias\" is not actually a COLLECT output column (e.g. it collides with a regular GROUPBY reducer alias, a LOAD field, or an APPLY expression). Also possible after a server/protocol mismatch where the alias resolves to a scalar or a raw map instead of an array.","commonSituations":"Alias name collision: the COLLECT AS alias matches another reducer's alias or a projected field name. Reading a RESP3 vs RESP2 reply through an unexpected protocol setting. Server version that does not support COLLECT (requires Redis 8.8+ with search-enable-unstable-features) returning an error-shaped scalar under the same key. Calling Collect on an alias that was never declared as a COLLECT reducer.","solutions":["Verify the alias passed to row.Collect matches the AS alias of a COLLECT reducer you actually added (and does not collide with other reducers/fields).","Check REDIS_VERSION / search-enable-unstable-features is enabled on the server; COLLECT needs Redis 8.8+.","Inspect row.Fields[alias] (or cmd.RawVal()) to see the actual type and shape returned by the server, then correct the alias or the reducer setup.","If the alias legitimately holds a non-array under some rows, branch on type before calling Collect instead of assuming the shape."],"exampleFix":"// before\ncol, err := row.Collect(\"price\") // \"price\" is a plain LOAD field, not COLLECT\n// after\ncol, err := row.Collect(\"collected_items\") // matches NewCollectReducer(... As: \"collected_items\")","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isCollectColumn(v interface{}) bool {\n\tif v == nil { return true }\n\t_, ok := v.([]interface{})\n\treturn ok\n}\n\n// guard before calling Collect:\nif v, ok := row.Fields[alias]; ok && !isCollectColumn(v) {\n    return fmt.Errorf(\"alias %q is not a COLLECT column: %T\", alias, v)\n}","tryCatchPattern":"col, err := row.Collect(alias)\nif err != nil {\n    // alias is not a COLLECT column or server reply is malformed;\n    // fall back to reading the raw value or skip the row\n    log.Printf(\"collect decode failed for %q: %v\", alias, err)\n    return nil, err\n}","preventionTips":["Use a unique AS alias for each COLLECT reducer that does not collide with LOAD/APPLY/other reducers.","Confirm the server supports COLLECT (Redis 8.8+ with search-enable-unstable-features) before relying on it.","Inspect cmd.RawVal() when this fires to confirm the actual server-side shape."],"tags":["ft-aggregate","collect","response-decoding","type-mismatch"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}