{"id":"9920f1281a2b379d","repo":"go-redis/redis","slug":"redis-collect-entry-d-w","errorCode":null,"errorMessage":"redis: COLLECT entry %d: %w","messagePattern":"redis: COLLECT entry (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_collect.go","lineNumber":198,"sourceCode":"\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) {\n\tcase map[interface{}]interface{}: // RESP3\n\t\tout := make(CollectEntry, len(m))\n\t\tfor k, val := range m {\n\t\t\tout[fmt.Sprint(k)] = val\n\t\t}\n\t\treturn out, nil","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_collect.go#L180-L216","documentation":"Wraps a per-entry decoding failure inside parseCollectValue: the COLLECT alias value is an array, but element at index %d could not be decoded by parseCollectEntry (odd-length key/value array or unexpected entry type). The '%w' verb preserves the underlying error for errors.Is/As. The index is zero-based into the server-returned entry array.","triggerScenarios":"AggregateRow.Collect succeeds in finding the alias and reading it as an array, but one of the entries is malformed (e.g. a bare string/number instead of a map or flat array, or a RESP2 flat array with an odd element count). Triggered during response decoding, so the server reply shape is the root cause.","commonSituations":"Server version/protocol drift producing a non-conforming COLLECT entry. RESP2/RESP3 negotiation mismatch where entries arrive in an unexpected representation. A schema change causing one entry to be projected differently. Proxy or resp-mangling middleware corrupting the frame.","solutions":["Inspect cmd.RawVal() to see the exact shape of the offending entry at the reported index.","Confirm the server supports COLLECT (Redis 8.8+) and that the protocol (RESP2 vs RESP3) matches what the client negotiated.","If you cannot upgrade the server, avoid COLLECT or reduce the projected fields to isolate which field triggers the bad shape.","Reproduce with redis-cli / RESP3 to determine whether the malformed entry comes from the server or from an intermediate proxy."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"col, err := row.Collect(alias)\nif err != nil {\n    // one of the entries was malformed; the wrapped error names entry index and cause\n    var decodeErr *collectEntryError // if you wrap/define one\n    if errors.As(err, &decodeErr) { /* handle specific shape */ }\n    return fmt.Errorf(\"COLLECT decode failed on alias %q: %w\", alias, err)\n}","preventionTips":["Pin the server Redis version that supports COLLECT (8.8+) in your test/prod images.","Match the client's negotiated protocol to the server's reply type (RESP2/RESP3).","Capture cmd.RawVal() in telemetry when COLLECT decoding fails to identify the offending entry."],"tags":["ft-aggregate","collect","response-decoding","server-mismatch"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}