{"id":"30514e88f4f6591b","repo":"go-redis/redis","slug":"redis-tx-pipeline-produced-no-outcome","errorCode":null,"errorMessage":"redis: tx pipeline produced no outcome","messagePattern":"redis: tx pipeline produced no outcome","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"osscluster.go","lineNumber":2429,"sourceCode":"\t\t\t// Connection acquisition failed — fn never ran.\n\t\t\tif shouldRetry(err, true) && !cmdsContainNoRetry(cmds) {\n\t\t\t\toutcome = &txOutcome{kind: txRetryConn, err: err}\n\t\t\t} else {\n\t\t\t\toutcome = &txOutcome{kind: txFatal, err: err}\n\t\t\t}\n\t\t}\n\t\treturn err\n\t})\n\n\tif !executed && chainErr != nil {\n\t\t// A node-level hook aborted with an error: surface its verdict. A hook\n\t\t// that returned nil short-circuited successfully (it served the batch),\n\t\t// which is legal for plain pipelines too, so it is not turned into a\n\t\t// fatal outcome (review finding by codex on #3942).\n\t\toutcome = &txOutcome{kind: txFatal, err: chainErr}\n\t}\n\tif outcome == nil {\n\t\toutcome = &txOutcome{kind: txFatal, err: fmt.Errorf(\"redis: tx pipeline produced no outcome\")}\n\t}\n\treturn outcome\n}\n\nfunc (c *ClusterClient) processTxPipelineNodeConn(\n\tctx context.Context, node *clusterNode, cn *pool.Conn, wire []Cmder, cmds []Cmder, asking bool,\n) *txOutcome {\n\t// HIMPORT bookkeeping: pending discards and PREPAREs for registered\n\t// fieldsets the transaction references get written ahead of the wire\n\t// batch (before ASKING/MULTI; the session state is visible at EXEC).\n\tinjected := node.Client.himportInjectedCmds(ctx, cn, cmds)\n\n\tif err := cn.WithWriter(c.context(ctx), c.opt.WriteTimeout, func(wr *proto.Writer) error {\n\t\tfor _, ic := range injected {\n\t\t\tif err := writeCmd(wr, ic); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}","sourceCodeStart":2411,"sourceCodeEnd":2447,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/osscluster.go#L2411-L2447","documentation":"A defensive fatal outcome raised in the cluster tx-pipeline path when, after running the connection-acquire closure and checking hook results, the outcome variable is still nil. It marks a logic gap where neither success, retry, redirect, nor an earlier error classified the result. Under normal operation it should be unreachable.","triggerScenarios":"Triggered only by an internal inconsistency in tx-pipeline outcome classification (e.g. a hook short-circuit path that does not set an outcome). Not something application input can target directly.","commonSituations":"Seen after upgrading go-redis if a regression leaves an unhandled code path, or with a custom hook that returns in a way the classifier does not expect. Essentially a bug sentinel.","solutions":["Report it as a go-redis bug with the command sequence and hook configuration that triggered it.","Temporarily simplify or remove custom ProcessHook/ProcessPipelineHook to see if the path resolves.","Downgrade to a known-good go-redis version while the regression is investigated."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := cmd.Err(); err != nil && strings.Contains(err.Error(), \"tx pipeline produced no outcome\") {\n    // retry the tx once; if it persists, file a go-redis issue with hooks/cmd details\n}","preventionTips":["Pin a known-good go-redis version in go.mod.","Minimise custom ProcessHook/ProcessPipelineHook that can short-circuit outcomes.","Reproduce with hooks disabled before reporting."],"tags":["cluster","pipeline","transaction","internal"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}