juicedata/juicefs · error

invalid HSCAN response for %s: odd number of elements: %d

Error message

invalid HSCAN response for %s: odd number of elements: %d

What it means

hscanToMap scans a Redis hash via HSCAN and expects each batch to be flat key/value pairs. Redis guarantees an even number of elements; receiving an odd count means the response is malformed (protocol/proxy problem or a wrapped/incompatible reply), so the function refuses to build a partially-wrong map and returns this error.

Source

Thrown at pkg/meta/redis.go:3978

		}
		if len(keys) > 0 {
			if err = f(keys); err != nil {
				return err
			}
		}
		if c == 0 {
			break
		}
		cursor = c
	}
	return nil
}

func (m *redisMeta) hscanToMap(ctx context.Context, key string) (map[string]string, error) {
	result := make(map[string]string)
	err := m.hscan(ctx, key, func(keys []string) error {
		if len(keys)%2 != 0 {
			return fmt.Errorf("invalid HSCAN response for %s: odd number of elements: %d", key, len(keys))
		}
		for i := 0; i < len(keys); i += 2 {
			result[keys[i]] = keys[i+1]
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (m *redisMeta) CleanupSlices(ctx Context) syscall.Errno {
	logger.Debugf("start cleanup...")
	m.cleanupLeakedInodes(true)
	m.cleanupLeakedChunks(true)
	m.cleanupOldSliceRefs(true)
	return m.baseMeta.CleanupSlices(ctx)

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Identify the middleware/proxy between the client and Redis and test HSCAN against real Redis directly (redis-cli HSCAN key 0).
  2. Upgrade or bypass the non-conforming proxy/compatible server.
  3. Retry the scan; a transient truncated reply may succeed on retry.
  4. If reproducible, capture the reply (MONITOR or tcpdump) and report to the proxy/storage vendor.
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check the backend conforms
keys, err := doRawHscan(testKey)
if err != nil || len(keys)%2 != 0 { log.Fatal("HSCAN replies are non-conforming; check proxy/compatible server") }

Try / catch

m, err := hscanToMap(ctx, key)
if err != nil && strings.Contains(err.Error(), "invalid HSCAN response") {
    logger.Warnf("malformed HSCAN from %s, retrying once", key)
    m, err = hscanToMap(ctx, key)
}

Prevention

When it happens

Trigger: HSCAN on a hash (used by cleanup/maintenance scans over delSlices/sliceRefs-style keys) returning a keys slice whose length is not even — typically from a broken proxy, custom Redis-compatible server, or corrupted reply handling.

Common situations: Using Redis-compatible stores (KeyDB, Dragonfly, older Twemproxy/Codis) with non-conforming HSCAN replies; custom network middleware mangling multi-bulk replies.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/f004cf962072bec2. Report an issue: GitHub.