go-redis/redis · error

odd-length key/value array of length %d

Error message

odd-length key/value array of length %d

What it means

Returned by parseCollectEntry in the RESP2 branch when a flat key/value array has an odd number of elements. RESP2 represents each COLLECT entry as [field, value, field, value, ...], so an odd length means the frame is truncated or corrupt. The length is included to show how off it is.

Source

Thrown at search_collect.go:221

}

// parseCollectEntry decodes a single collected entry from either the RESP3
// map form or the RESP2 flat key/value array form into a CollectEntry. Keys
// are passed through as-is: the server already returns them without the "@"
// prefix.
func parseCollectEntry(e interface{}) (CollectEntry, error) {
	switch m := e.(type) {
	case map[interface{}]interface{}: // RESP3
		out := make(CollectEntry, len(m))
		for k, val := range m {
			out[fmt.Sprint(k)] = val
		}
		return out, nil
	case map[string]interface{}: // already string-keyed
		return m, nil
	case []interface{}: // RESP2 flat [field, value, field, value, ...]
		if len(m)%2 != 0 {
			return nil, fmt.Errorf("odd-length key/value array of length %d", len(m))
		}
		out := make(CollectEntry, len(m)/2)
		for i := 0; i < len(m); i += 2 {
			key, ok := m[i].(string)
			if !ok {
				key = fmt.Sprint(m[i])
			}
			out[key] = m[i+1]
		}
		return out, nil
	default:
		return nil, fmt.Errorf("unexpected type %T, want map or key/value array", e)
	}
}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Check the server Redis version and patch level against the COLLECT feature requirements (8.8+).
  2. Capture the raw reply (cmd.RawResult()) and inspect the offending entry to confirm truncation.
  3. If a proxy is in front of Redis, bypass it to see if the corruption originates upstream.
  4. Report to the server/proxy maintainer with the raw frame; this shape should never be emitted by a conforming COLLECT implementation.
Defensive patterns

Strategy: try-catch

Type guard

func isEvenKVArray(e interface{}) bool {
	arr, ok := e.([]interface{})
	return ok && len(arr)%2 == 0
}

Try / catch

col, err := row.Collect(alias)
if err != nil && strings.Contains(err.Error(), "odd-length key/value array") {
    // server/proxy returned a truncated RESP2 entry; report raw frame
    raw, _ := cmd.RawResult()
    log.Printf("malformed COLLECT entry, raw=%v", raw)
}
return err

Prevention

When it happens

Trigger: AggregateRow.Collect decoding a RESP2 reply where one entry array has odd length (e.g. 3 or 5 elements). Surfaces during response decoding only; cannot be produced by local API misuse.

Common situations: A server/proxy bug truncating the entry array. RESP2/RESP3 confusion where a partial map leaks through as a half-array. Field count mismatch between what COLLECT projected and what the server emitted. Network-level frame corruption (rare).

Related errors


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