{"id":"058fae4fe4dbb8c4","repo":"go-redis/redis","slug":"redis-cannot-pipeline-command-q-with-request-pol","errorCode":null,"errorMessage":"redis: cannot pipeline command %q with request policy ReqAllNodes/ReqAllShards/ReqMultiShard; Note: This behavior is subject to change in the future","messagePattern":"redis: cannot pipeline command %q with request policy ReqAllNodes/ReqAllShards/ReqMultiShard; Note: This behavior is subject to change in the future","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"osscluster.go","lineNumber":1692,"sourceCode":"// instead of splitting every batch across all nodes at flush. Cluster slots are\n// contiguous per node, so bucketing by slot range (slot*shards/16384) keeps a\n// node's slots together. Keyless commands hash to slot -1 → bucket 0; multi-node\n// commands are already rejected from pipelines, so only single-node commands\n// reach here.\nfunc (c *ClusterClient) installAutoPipelineSharding(ap *AutoPipeliner) {\n\t// Reject commands whose request policy cannot ride a pipeline (ReqAllNodes/\n\t// ReqAllShards/ReqMultiShard) at submit, BEFORE they can join a merged\n\t// batch: mapCmdsByNode fails a whole mapping on such a command (user\n\t// pipelines are all-or-nothing), and one autopipeline caller must not be\n\t// able to poison unrelated callers' batches. Rejecting here also keeps the\n\t// lone-command fast path consistent with batched dispatch — the command is\n\t// refused regardless of what it happens to coalesce with.\n\tap.setPreflight(func(ctx context.Context, cmd Cmder) error {\n\t\tif c.cmdInfoResolver == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif policy := c.cmdInfoResolver.GetCommandPolicy(ctx, cmd); policy != nil && !policy.CanBeUsedInPipeline() {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"redis: cannot pipeline command %q with request policy ReqAllNodes/ReqAllShards/ReqMultiShard; Note: This behavior is subject to change in the future\", cmd.Name(),\n\t\t\t)\n\t\t}\n\t\treturn nil\n\t})\n\t// Commands whose routing is not slot-derived must not be coalesced: a solo\n\t// flush reaches ClusterClient.process and its special handling (FT.CURSOR\n\t// READ/DEL are sticky to the node holding the cursor), but inside a batch\n\t// mapCmdsByNode routes by slot and can hit the wrong shard — visible only\n\t// under concurrent traffic, which is the worst way to find it. Divert them\n\t// instead of rejecting: they work fine on their own connection (review\n\t// finding by codex on #3942).\n\tap.setMustDivert(func(ctx context.Context, cmd Cmder) bool {\n\t\tif c.cmdInfoResolver == nil {\n\t\t\treturn false\n\t\t}\n\t\tpolicy := c.cmdInfoResolver.GetCommandPolicy(ctx, cmd)\n\t\treturn policy != nil && policy.Request == routing.ReqSpecial","sourceCodeStart":1674,"sourceCodeEnd":1710,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/osscluster.go#L1674-L1710","documentation":"Returned by the autopipeline preflight (installAutoPipelineSharding) when a command whose request policy is ReqAllNodes, ReqAllShards, or ReqMultiShard is submitted through an AutoPipeline/AsyncAutoPipeline face on a ClusterClient. Such commands fan out across nodes and cannot be merged into a single-node batch, so they are rejected at submit before poisoning a coalesced batch.","triggerScenarios":"Issuing a fan-out command (e.g. FLUSHALL, CONFIG GET, INFO across the cluster, or a multi-shard command) through client.AutoPipeline() on a ClusterClient. The error names the offending command via cmd.Name().","commonSituations":"Migrating standalone code to cluster + autopipelining without realising certain admin/aggregate commands are incompatible, or wrapping every call including cluster-wide ones in an autopipeline batch.","solutions":["Run fan-out commands directly on the cluster client (not through the AutoPipeline face).","Use ClusterClient.Process or the dedicated helper for the operation instead of pipelining it.","If you need it in a batch, split the work into per-slot commands that are single-node."],"exampleFix":"// before\nap := client.AutoPipeline()\nap.Do(ctx, \"FLUSHALL\")\nap.Exec(ctx)\n// after\nclient.Do(ctx, \"FLUSHALL\")","handlingStrategy":"try-catch","validationCode":"func isFanOutCommand(ctx context.Context, c *redis.ClusterClient, cmd redis.Cmder) bool {\n    // Heuristic: admin/aggregate names that the cluster router fans out.\n    switch strings.ToUpper(cmd.Name()) {\n    case \"FLUSHALL\", \"FLUSHDB\", \"CONFIG\", \"INFO\", \"KEYS\", \"DBSIZE\", \"WAIT\", \"SCAN\":\n        return true\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"err := ap.Do(ctx, \"...\").Err()\nif err != nil && strings.Contains(err.Error(), \"cannot pipeline command\") {\n    // re-issue directly on the cluster client outside the autopipeline\n}","preventionTips":["Do not route fan-out/admin commands through AutoPipeline.","Keep autopipeline batches to single-slot keyed commands.","Isolate cluster-wide operations in their own helper that bypasses the pipeline."],"tags":["cluster","pipeline","routing"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}