{"id":"60a537bc98fca660","repo":"go-redis/redis","slug":"redis-autopipelineoptions-numshards-d-requires-u","errorCode":null,"errorMessage":"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)","messagePattern":"redis: AutoPipelineOptions\\.NumShards=(.+?) requires Unordered:true on the deferred \\(async\\) face \\(commands are distributed round-robin across shards, which flush concurrently and do not preserve submit order\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"autopipeline.go","lineNumber":781,"sourceCode":"\tif config.MaxBatchSize <= 0 {\n\t\tconfig.MaxBatchSize = 200\n\t}\n\n\tif config.MaxConcurrentBatches <= 0 {\n\t\t// Default to an ordered single stream. Callers raise this (with\n\t\t// Unordered:true) to opt into parallel-batch throughput.\n\t\tconfig.MaxConcurrentBatches = 1\n\t}\n\n\t// NumShards > 1 on the deferred (async) face distributes commands\n\t// round-robin across shards that flush concurrently, so submit order is\n\t// not preserved — require the explicit Unordered opt-in, exactly like\n\t// MaxConcurrentBatches > 1. The blocking face is exempt (each caller waits\n\t// per command, and Submit is rejected there), as is cluster slot sharding\n\t// (contentSharded: same-key commands always land in the same shard, so\n\t// per-key order holds).\n\tif config.NumShards > 1 && !config.Unordered && !blocking && !config.contentSharded {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"redis: AutoPipelineOptions.NumShards=%d requires Unordered:true on the deferred (async) face \"+\n\t\t\t\t\"(commands are distributed round-robin across shards, which flush concurrently and do not preserve submit order)\",\n\t\t\tconfig.NumShards)\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tap := &AutoPipeliner{\n\t\tpipeliner: pipeliner,\n\t\tconfig:    config,\n\t\tblocking:  blocking,\n\t\tctx:       ctx,\n\t\tcancel:    cancel,\n\t}\n\n\t// Route the typed command surface. Blocking: the command call blocks until\n\t// executed (synchronous drop-in shape). Deferred: the call returns at once\n\t// and the result accessors block until the batch executes.","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/autopipeline.go#L763-L799","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add Unordered: true if your workload tolerates out-of-order completion (e.g. independent cache fills, idempotent sets).","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.","Move that workload to the blocking face, which is exempt from the Unordered requirement."],"exampleFix":"// before\nopts := redis.AutoPipelineOptions{NumShards: 4}\nap, err := client.AutoPipeline(ctx, opts) // async face\n\n// after (option A — opt into unordered)\nopts := redis.AutoPipelineOptions{NumShards: 4, Unordered: true}\n\n// after (option B — keep order, drop sharding)\nopts := redis.AutoPipelineOptions{NumShards: 1}","handlingStrategy":"validation","validationCode":"if opts.NumShards > 1 && !opts.Unordered && !opts.contentSharded {\n    // either opt in, or drop sharding\n    opts.Unordered = true // only if your workload tolerates out-of-order completion\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide ordering semantics BEFORE raising NumShards.","Default NumShards to 0/1 — the single shard is throughput-optimal for ordered workloads.","Document near the config knob why Unordered is required for multi-shard async."],"tags":["autopipeline","config-validation","ordering","concurrency"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}