go-redis/redis · error

redis: AutoPipelineOptions.NumShards=%d requires Unordered:t

Error message

redis: AutoPipelineOptions.NumShards=%d requires Unordered:true on the deferred (async) face (commands are distributed round-robin across shards, which flush concurrently and do not preserve submit order)

What it means

Returned when the deferred (async) AutoPipeliner face is built with NumShards > 1 but Unordered is false. With multiple shards, commands are distributed round-robin across shards that flush concurrently, so the order in which results become available does not match submit order. The library requires the caller to opt into that semantics explicitly via Unordered: true. The blocking face and cluster content-sharding are exempt because per-key order is still preserved there.

Source

Thrown at autopipeline.go:781

	if config.MaxBatchSize <= 0 {
		config.MaxBatchSize = 200
	}

	if config.MaxConcurrentBatches <= 0 {
		// Default to an ordered single stream. Callers raise this (with
		// Unordered:true) to opt into parallel-batch throughput.
		config.MaxConcurrentBatches = 1
	}

	// NumShards > 1 on the deferred (async) face distributes commands
	// round-robin across shards that flush concurrently, so submit order is
	// not preserved — require the explicit Unordered opt-in, exactly like
	// MaxConcurrentBatches > 1. The blocking face is exempt (each caller waits
	// per command, and Submit is rejected there), as is cluster slot sharding
	// (contentSharded: same-key commands always land in the same shard, so
	// per-key order holds).
	if config.NumShards > 1 && !config.Unordered && !blocking && !config.contentSharded {
		return nil, fmt.Errorf(
			"redis: AutoPipelineOptions.NumShards=%d requires Unordered:true on the deferred (async) face "+
				"(commands are distributed round-robin across shards, which flush concurrently and do not preserve submit order)",
			config.NumShards)
	}

	ctx, cancel := context.WithCancel(context.Background())

	ap := &AutoPipeliner{
		pipeliner: pipeliner,
		config:    config,
		blocking:  blocking,
		ctx:       ctx,
		cancel:    cancel,
	}

	// Route the typed command surface. Blocking: the command call blocks until
	// executed (synchronous drop-in shape). Deferred: the call returns at once
	// and the result accessors block until the batch executes.

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Add Unordered: true if your workload tolerates out-of-order completion (e.g. independent cache fills, idempotent sets).
  2. Keep NumShards at 0 or 1 (the default) if you need submit-order preservation — the default single shard is the documented throughput-optimal choice anyway.
  3. Move that workload to the blocking face, which is exempt from the Unordered requirement.

Example fix

// before
opts := redis.AutoPipelineOptions{NumShards: 4}
ap, err := client.AutoPipeline(ctx, opts) // async face

// after (option A — opt into unordered)
opts := redis.AutoPipelineOptions{NumShards: 4, Unordered: true}

// after (option B — keep order, drop sharding)
opts := redis.AutoPipelineOptions{NumShards: 1}
Defensive patterns

Strategy: validation

Validate before calling

if opts.NumShards > 1 && !opts.Unordered && !opts.contentSharded {
    // either opt in, or drop sharding
    opts.Unordered = true // only if your workload tolerates out-of-order completion
}

Prevention

When it happens

Trigger: Calling the async/deferred AutoPipeliner constructor with NumShards > 1 (e.g. 4) and Unordered omitted (false), and the build is not blocking and not contentSharded. Fires at autopipeline.go:780 during NewAutoPipeliner.

Common situations: Raising NumShards to chase throughput without reading the ordering contract; porting a blocking-face config to the deferred face; assuming the library will silently serialize flushes across shards.

Related errors


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