etcd-io/etcd · error
cannot call If after Else!
Error message
cannot call If after Else!
What it means
The txn builder panics with 'cannot call If after Else!' when If() is invoked after Else() on the same Txn. The Txn grammar permits exactly one ordered If/Then/Else sequence; the celse flag marks Else as declared, and a subsequent If is rejected with a panic under the txn mutex. The transaction has not touched the network at this point.
Source
Thrown at client/v3/txn.go:86
fas []*pb.RequestOp
callOpts []grpc.CallOption
}
func (txn *txn) If(cs ...Cmp) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cif {
panic("cannot call If twice!")
}
if txn.cthen {
panic("cannot call If after Then!")
}
if txn.celse {
panic("cannot call If after Else!")
}
txn.cif = true
for i := range cs {
cmp := cs[i].Clone()
txn.cmps = append(txn.cmps, cmp.GetCompare())
}
return txn
}
func (txn *txn) Then(ops ...Op) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cthen {
panic("cannot call Then twice!")View on GitHub (pinned to f744d457f4)
Solutions
- Move the If call before Then/Else in the chain
- Assemble conditions and both branches as slices first, then run the single chain If(...).Then(...).Else(...) once
- Avoid passing live Txn objects between layers; pass data, build the Txn in one function
- Unit-test composite transaction construction paths
Example fix
// before
t := cli.Txn(ctx).Then(clientv3.OpPut("k", "v")).Else(clientv3.OpGet("k"))
t = t.If(clientv3.Compare(clientv3.Version("k"), "=", 0)) // panics
// after
t := cli.Txn(ctx).
If(clientv3.Compare(clientv3.Version("k"), "=", 0)).
Then(clientv3.OpPut("k", "v")).
Else(clientv3.OpGet("k")) Defensive patterns
Strategy: validation
Validate before calling
// Decide conditions before building any branch:
if cond {
t = cli.Txn(ctx).If(cmp).Then(a).Else(b)
} else {
t = cli.Txn(ctx).Then(a).Else(b)
} Prevention
- Never call If after Else
- Compute conditions up front
- Keep Txn construction in exactly one place
When it happens
Trigger: Sequences like cli.Txn(ctx).Then(a).Else(b).If(c) — calling If after Else (with or without Then in between). Usually caused by helper layers appending conditions late, or by code that builds the fallback branch first and decides conditions afterwards.
Common situations: Framework/ORM-style wrappers where user code sets branches and infrastructure adds conditions later; refactors that moved If below Else; copy-paste of an If block into the wrong position in a builder chain.
Related errors
- cannot call If after Then!
- cannot call Then after Else!
- cannot call If twice!
- cannot call Then twice!
- cannot call Else twice!
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/27a1d8ab2211b743.
Report an issue: GitHub.