go-redis/redis · error

redis: AutoPipelineOptions.MaxFlushDelay=%s must be >= 0

Error message

redis: AutoPipelineOptions.MaxFlushDelay=%s must be >= 0

What it means

Returned by AutoPipelineOptions.Validate when MaxFlushDelay is negative. Zero is allowed (meaning no coalescing delay — flush immediately); negative durations are rejected as configuration errors. MaxFlushDelay is a time.Duration; the error formats it with %s.

Source

Thrown at autopipeline.go:234

	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)
	}
	if cfg.NumShards < 0 {
		return fmt.Errorf("redis: AutoPipelineOptions.NumShards=%d must be >= 0", cfg.NumShards)
	}
	if cfg.AdaptiveDelay && cfg.MaxFlushDelay <= 0 {
		return fmt.Errorf("redis: AutoPipelineOptions.AdaptiveDelay requires MaxFlushDelay > 0 " +
			"(adaptive delay scales MaxFlushDelay by queue fill; with no MaxFlushDelay it would " +
			"silently disable batch accumulation entirely)")
	}
	return nil
}

// cmdableClient is an interface for clients that support pipelining.
// Both Client and ClusterClient implement this interface. It embeds
// UniversalClient (Cmdable + Process + Do + AddHook + Watch + Subscribe... +
// Close + PoolStats) so the AutoPipeliner can delegate the non-batched surface
// back to the underlying client and itself satisfy UniversalClient.
type cmdableClient interface {

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Set MaxFlushDelay to 0 (no coalescing wait, default) or a positive duration like 100 * time.Microsecond.
  2. Use 0 for 'no delay' rather than a negative.
  3. Guard duration computations to clamp at zero minimum.

Example fix

// before
cfg := &redis.AutoPipelineOptions{MaxFlushDelay: -100 * time.Microsecond}
// cfg.Validate() => error

// after
cfg := &redis.AutoPipelineOptions{MaxFlushDelay: 0}                    // no delay
// or
cfg := &redis.AutoPipelineOptions{MaxFlushDelay: 100 * time.Microsecond} // batch coalescing
Defensive patterns

Strategy: validation

Validate before calling

func sanitizeFlushDelay(d time.Duration) time.Duration {
    if d < 0 {
        return 0 // no delay instead of an invalid negative
    }
    return d
}

Try / catch

if err := cfg.Validate(); err != nil {
    if strings.Contains(err.Error(), "MaxFlushDelay") {
        cfg.MaxFlushDelay = 0 // reset to no-delay default
    }
}

Prevention

When it happens

Trigger: Setting AutoPipelineOptions.MaxFlushDelay to a negative time.Duration (e.g. -1 * time.Millisecond, or a zero-value subtraction that underflows). Config parsing that yields a negative duration.

Common situations: Duration arithmetic that produces a negative. Config files encoding a negative delay. Using a negative to mean 'disabled' when zero is the documented 'no delay' value.

Related errors


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