{"id":"942af52ae32f8ca2","repo":"go-redis/redis","slug":"redis-autopipelineoptions-adaptivedelay-requires","errorCode":null,"errorMessage":"redis: AutoPipelineOptions.AdaptiveDelay requires MaxFlushDelay > 0 (adaptive delay scales MaxFlushDelay by queue fill; with no MaxFlushDelay it would silently disable batch accumulation entirely)","messagePattern":"redis: AutoPipelineOptions\\.AdaptiveDelay requires MaxFlushDelay > 0 \\(adaptive delay scales MaxFlushDelay by queue fill; with no MaxFlushDelay it would silently disable batch accumulation entirely\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"autopipeline.go","lineNumber":240,"sourceCode":"\t// than being silently coerced to a default. Zero is allowed and means \"use\n\t// the default\" (MaxBatchSize) or \"no delay\" (MaxFlushDelay).\n\tif cfg.MaxBatchSize < 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.MaxBatchSize=%d must be >= 0\", cfg.MaxBatchSize)\n\t}\n\tif cfg.MaxBatchBytes < 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.MaxBatchBytes=%d must be >= 0\", cfg.MaxBatchBytes)\n\t}\n\tif cfg.MaxConcurrentBatches < 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.MaxConcurrentBatches=%d must be >= 0\", cfg.MaxConcurrentBatches)\n\t}\n\tif cfg.MaxFlushDelay < 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.MaxFlushDelay=%s must be >= 0\", cfg.MaxFlushDelay)\n\t}\n\tif cfg.NumShards < 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.NumShards=%d must be >= 0\", cfg.NumShards)\n\t}\n\tif cfg.AdaptiveDelay && cfg.MaxFlushDelay <= 0 {\n\t\treturn fmt.Errorf(\"redis: AutoPipelineOptions.AdaptiveDelay requires MaxFlushDelay > 0 \" +\n\t\t\t\"(adaptive delay scales MaxFlushDelay by queue fill; with no MaxFlushDelay it would \" +\n\t\t\t\"silently disable batch accumulation entirely)\")\n\t}\n\treturn nil\n}\n\n// cmdableClient is an interface for clients that support pipelining.\n// Both Client and ClusterClient implement this interface. It embeds\n// UniversalClient (Cmdable + Process + Do + AddHook + Watch + Subscribe... +\n// Close + PoolStats) so the AutoPipeliner can delegate the non-batched surface\n// back to the underlying client and itself satisfy UniversalClient.\ntype cmdableClient interface {\n\tUniversalClient\n\t// processPipelineHook is the hook-wrapped []Cmder pipeline entry — the same\n\t// method Pipeline.Exec is wired to (see Client.Pipeline). The flusher\n\t// dispatches drained batches through it directly, skipping the per-batch\n\t// Pipeline construction; hooks/OTel see the identical call.\n\tprocessPipelineHook(ctx context.Context, cmds []Cmder) error","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/autopipeline.go#L222-L258","documentation":"Returned by AutoPipelineOptions.Validate() when AdaptiveDelay is enabled but MaxFlushDelay is zero or negative. AdaptiveDelay works by scaling MaxFlushDelay according to how full the queue is, so a zero MaxFlushDelay would collapse to zero delay on every flush and silently disable batch accumulation entirely. The library refuses the config at construction time rather than pretend to honor it.","triggerScenarios":"Constructing an AutoPipeliner (or calling AutoPipeline() on a client) with AutoPipelineOptions{AdaptiveDelay: true} while leaving MaxFlushDelay at its zero default (or setting it negative). The check at autopipeline.go:239 fires before any shard is created.","commonSituations":"Copy-pasting a config snippet that mentions AdaptiveDelay without the matching MaxFlushDelay; flipping AdaptiveDelay on while tuning other knobs; assuming AdaptiveDelay has a built-in default delay (it does not — it only scales an explicit MaxFlushDelay).","solutions":["Set MaxFlushDelay to a small positive duration (e.g. 50*time.Microsecond or 1*time.Millisecond) alongside AdaptiveDelay: true.","If you do not need adaptive batching, drop AdaptiveDelay (leave it false) and rely on the default backpressure-based coalescing.","Run AutoPipelineOptions.Validate() explicitly in a unit test over your config struct so this surfaces before deploy."],"exampleFix":"// before\nopts := redis.AutoPipelineOptions{\n    AdaptiveDelay: true,\n    // MaxFlushDelay left zero\n}\n\n// after\nopts := redis.AutoPipelineOptions{\n    AdaptiveDelay:  true,\n    MaxFlushDelay:  200 * time.Microsecond,\n}","handlingStrategy":"validation","validationCode":"if opts.AdaptiveDelay && opts.MaxFlushDelay <= 0 {\n    return fmt.Errorf(\"AdaptiveDelay needs a positive MaxFlushDelay (got %s)\", opts.MaxFlushDelay)\n}\nif err := opts.Validate(); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair AdaptiveDelay with an explicit positive MaxFlushDelay.","Call AutoPipelineOptions.Validate() in a unit test over your config struct.","Treat config as code: review the full struct, not just the field you changed."],"tags":["autopipeline","config-validation","batching"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}