redis/go-redis · error

odd-length key/value array of length %d

Error message

odd-length key/value array of length %d

What it means

Under RESP2 a COLLECT entry may arrive as a flat array [field, value, field, value, ...]. parseCollectEntry requires an even number of elements so each field pairs with a value; an odd-length array cannot be split into key/value pairs and fails with this error.

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 c5cad058c7)

Solutions

  1. Use RESP3 (Protocol: 3) so entries arrive as maps and this flat-array path is avoided
  2. Fix the proxy/truncation issue that dropped the trailing value
  3. Check the wrapped index from "redis: COLLECT entry %d" to locate the malformed row

Example fix

// before
[]interface{}{"field1", "v1", "field2"} // odd length
// after
[]interface{}{"field1", "v1", "field2", "v2"} // or switch to RESP3 maps
Defensive patterns

Strategy: type-guard

Type guard

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

Prevention

When it happens

Trigger: parseCollectEntry receiving a []interface{} of odd length (e.g. a truncated or nil-valued final field) from a COLLECT reply; len(m)%2 != 0.

Common situations: RESP2 replies where a trailing value was dropped or a proxy mangled the entry; manually constructed test replies with a missing value.

Related errors


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/00f9f0c8e1d29512. Report an issue: GitHub.