etcd-io/etcd · error
cannot call If after Then!
Error message
cannot call If after Then!
What it means
The txn builder panics with 'cannot call If after Then!' when If() is called on a Txn that has already had Then() called. etcd's Txn grammar is strictly ordered If -> Then -> Else; the cthen flag records that Then was declared, and any later If is a programming error that fails fast with a panic. No RPC has been sent when this fires.
Source
Thrown at client/v3/txn.go:82
cmps []*pb.Compare
sus []*pb.RequestOp
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()View on GitHub (pinned to f744d457f4)
Solutions
- Reorder the calls so all If(...) come before Then(...)/Else(...) on that Txn instance
- Build conditions and branches as local slices, then assemble Txn in one place: If(cs...).Then(ops...).Else(ops...)
- Make helper functions pure (return Cmp/Op slices) so ordering cannot be violated piecemeal
- Add a unit test per composite transaction your code builds to catch ordering bugs in CI
Example fix
// before
t := cli.Txn(ctx).Then(clientv3.OpPut("k", "v"))
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")) Defensive patterns
Strategy: validation
Validate before calling
// Assemble data first, then the chain in order: t := cli.Txn(ctx). If(cmps...). Then(thenOps...). Else(elseOps...) // If always before Then/Else
Prevention
- Enforce If -> Then -> Else ordering in one builder function
- Do not pass Txn objects across layers for later mutation
- Unit-test composite transactions
When it happens
Trigger: Sequences like cli.Txn(ctx).Then(op).If(cmp), typically caused by calling If after a Then in reordered code, or by a helper that unconditionally adds conditions but is invoked after the caller already built the Then branch.
Common situations: Refactoring that moves condition construction after branch construction; wrapper APIs where the caller sets Then/Else first and a framework hook injects If later; merge/refactor mistakes where lines get shuffled.
Related errors
- cannot call If after Else!
- 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/ede68325bb359730.
Report an issue: GitHub.