go-redis/redis · error
redis: AutoPipelineOptions.MaxConcurrentBatches=%d requires
Error message
redis: AutoPipelineOptions.MaxConcurrentBatches=%d requires Unordered:true (parallel batches do not preserve command ordering); set Unordered:true to allow it, or keep MaxConcurrentBatches=1 for an ordered stream
What it means
Returned by AutoPipelineOptions.Validate when MaxConcurrentBatches > 1 but Unordered is false. Parallel batches execute out of order, so the caller must explicitly opt into Unordered:true to acknowledge the loss of command ordering. With Unordered false (default), concurrency is forced to 1 (an ordered stream). This makes the ordering trade-off explicit rather than accidental.
Source
Thrown at autopipeline.go:217
func DefaultBlockingAutoPipelineOptions() *AutoPipelineOptions {
return &AutoPipelineOptions{
MaxBatchSize: 300,
MaxConcurrentBatches: 1,
}
}
// Validate reports whether the configuration is self-consistent. It returns an
// error if MaxConcurrentBatches > 1 without Unordered: true — raising
// concurrency gives up command ordering, so the caller must opt in explicitly.
//
// Validate()==nil does not guarantee construction succeeds: rules that need
// the face (e.g. NumShards>1 requires Unordered on the deferred face) are
// enforced by the AutoPipeline/AsyncAutoPipeline getters. Note also that
// Options.AutoPipelineOptions is validated lazily — on the first getter
// call, not in NewClient.
func (cfg *AutoPipelineOptions) Validate() error {
if cfg.MaxConcurrentBatches > 1 && !cfg.Unordered {
return fmt.Errorf("redis: AutoPipelineOptions.MaxConcurrentBatches=%d requires Unordered:true "+
"(parallel batches do not preserve command ordering); set Unordered:true to allow it, "+
"or keep MaxConcurrentBatches=1 for an ordered stream", cfg.MaxConcurrentBatches)
}
// Reject obviously-wrong negatives so a typo surfaces at construction rather
// than being silently coerced to a default. Zero is allowed and means "use
// the default" (MaxBatchSize) or "no delay" (MaxFlushDelay).
if cfg.MaxBatchSize < 0 {
return fmt.Errorf("redis: AutoPipelineOptions.MaxBatchSize=%d must be >= 0", cfg.MaxBatchSize)
}
if cfg.MaxBatchBytes < 0 {
return fmt.Errorf("redis: AutoPipelineOptions.MaxBatchBytes=%d must be >= 0", cfg.MaxBatchBytes)
}
if cfg.MaxConcurrentBatches < 0 {
return fmt.Errorf("redis: AutoPipelineOptions.MaxConcurrentBatches=%d must be >= 0", cfg.MaxConcurrentBatches)
}
if cfg.MaxFlushDelay < 0 {
return fmt.Errorf("redis: AutoPipelineOptions.MaxFlushDelay=%s must be >= 0", cfg.MaxFlushDelay)
}View on GitHub (pinned to 36d97525cd)
Solutions
- If you can tolerate out-of-order command execution, set Unordered: true alongside MaxConcurrentBatches > 1.
- If you need strict ordering, keep MaxConcurrentBatches = 1 (the default) and leave Unordered false.
- Call cfg.Validate() explicitly at startup to surface config errors early.
Example fix
// before — concurrency without opting out of ordering
cfg := &redis.AutoPipelineOptions{
MaxConcurrentBatches: 4, // Unordered defaults to false
}
// cfg.Validate() => error
// after — opt into unordered execution
cfg := &redis.AutoPipelineOptions{
MaxConcurrentBatches: 4,
Unordered: true,
}
// or keep ordering
cfg := &redis.AutoPipelineOptions{
MaxConcurrentBatches: 1,
} Defensive patterns
Strategy: validation
Validate before calling
func validateAutoPipelineOpts(cfg *redis.AutoPipelineOptions) error {
if cfg.MaxConcurrentBatches > 1 && !cfg.Unordered {
return fmt.Errorf("MaxConcurrentBatches=%d requires Unordered:true", cfg.MaxConcurrentBatches)
}
return cfg.Validate()
} Try / catch
if err := cfg.Validate(); err != nil {
if strings.Contains(err.Error(), "requires Unordered:true") {
// either set Unordered or reduce MaxConcurrentBatches to 1
}
} Prevention
- Call cfg.Validate() at startup to surface config errors before traffic.
- Only set Unordered:true when callers tolerate reordering (e.g. idempotent ops).
- Keep MaxConcurrentBatches=1 for ordered streams (the default).
When it happens
Trigger: Setting AutoPipelineOptions.MaxConcurrentBatches = 4 (or any value > 1) without also setting Unordered = true. Copying a high-concurrency config without reading the ordering implication. Validate() is called lazily on the first AutoPipeline/AsyncAutoPipeline getter, not in NewClient.
Common situations: Tuning throughput by raising MaxConcurrentBatches without realizing it breaks ordering. Windowed async callers that can tolerate reordering but forgot the flag. Config files shared across ordered and unordered workloads.
Related errors
- redis: AutoPipelineOptions.MaxBatchSize=%d must be >= 0
- redis: AutoPipelineOptions.MaxBatchBytes=%d must be >= 0
- redis: AutoPipelineOptions.MaxConcurrentBatches=%d must be >
- redis: AutoPipelineOptions.MaxFlushDelay=%s must be >= 0
- redis: AutoPipelineOptions.NumShards=%d must be >= 0
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/9da0d56d04c0e52b.json.
Report an issue: GitHub.