{"record":{"id":"920e6b162c9fa162","repo":"etcd-io/etcd","slug":"cannot-call-else-twice","errorCode":null,"errorMessage":"cannot call Else twice!","messagePattern":"cannot call Else twice!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/v3/txn.go","lineNumber":125,"sourceCode":"\t\tpanic(\"cannot call Then after Else!\")\n\t}\n\n\ttxn.cthen = true\n\n\tfor _, op := range ops {\n\t\ttxn.isWrite = txn.isWrite || op.isWrite()\n\t\ttxn.sus = append(txn.sus, op.toRequestOp())\n\t}\n\n\treturn txn\n}\n\nfunc (txn *txn) Else(ops ...Op) Txn {\n\ttxn.mu.Lock()\n\tdefer txn.mu.Unlock()\n\n\tif txn.celse {\n\t\tpanic(\"cannot call Else twice!\")\n\t}\n\n\ttxn.celse = true\n\n\tfor _, op := range ops {\n\t\ttxn.isWrite = txn.isWrite || op.isWrite()\n\t\ttxn.fas = append(txn.fas, op.toRequestOp())\n\t}\n\n\treturn txn\n}\n\nfunc (txn *txn) Commit() (*TxnResponse, error) {\n\ttxn.mu.Lock()\n\tdefer txn.mu.Unlock()\n\n\tr := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/etcd-io/etcd/blob/f744d457f484e9f748a0700b48ef96dcf792df33/client/v3/txn.go#L107-L143","documentation":"The txn builder panics with 'cannot call Else twice!' when Else() is called more than once on the same Txn. A transaction has a single else-branch; the celse flag turns a second Else into a fail-fast panic before any request is serialized. All else-ops must be passed together in one call.","triggerScenarios":"Calling t.Else(op1).Else(op2) on one Txn; loops that invoke Else per operation; multiple subsystems each adding fallback operations to a shared Txn instance.","commonSituations":"Code where fallback branches accumulate from several conditions or modules; translating nested if/else chains into one transaction; refactors splitting an Else call without collecting the ops into a slice.","solutions":["Pass all else-ops in a single call: Else(op1, op2, ...)","Collect []clientv3.Op for the fallback branch and call Else(ops...) once","Refactor helpers to return Op slices instead of chaining on a shared Txn","For genuinely separate fallback transactions, use separate Txn instances"],"exampleFix":"// before\nt := cli.Txn(ctx).If(c).Then(a).Else(clientv3.OpPut(\"x\", \"1\"))\nt = t.Else(clientv3.OpPut(\"y\", \"2\")) // panics\n\n// after\nt := cli.Txn(ctx).If(c).Then(a).Else(\n\tclientv3.OpPut(\"x\", \"1\"),\n\tclientv3.OpPut(\"y\", \"2\"),\n)","handlingStrategy":"validation","validationCode":"// One Else, all fallback ops together:\nvar elseOps []clientv3.Op\nfor _, k := range fallbackKeys {\n\telseOps = append(elseOps, clientv3.OpPut(k, \"v\"))\n}\nt := cli.Txn(ctx).If(c).Then(a).Else(elseOps...)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One Else per Txn","Accumulate fallback ops in a slice","Use separate transactions for independent fallbacks"],"tags":["etcd","clientv3","panic","api-misuse","txn","fluent-builder"],"backgroundTag":null,"analyzedSha":"f744d457f484e9f748a0700b48ef96dcf792df33","analyzedAt":"2026-08-15T09:39:50.079Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}