etcd-io/etcd · error

cannot call Then after Else!

Error message

cannot call Then after Else!

What it means

The txn builder panics with 'cannot call Then after Else!' when Then() is called after Else() has already been declared on the same Txn. The grammar order is If -> Then -> Else; once the else-branch is set (celse flag), adding a then-branch is a construction bug and the builder panics immediately. Nothing has been committed when this fires.

Source

Thrown at client/v3/txn.go:107

	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!")
	}
	if txn.celse {
		panic("cannot call Then after Else!")
	}

	txn.cthen = true

	for _, op := range ops {
		txn.isWrite = txn.isWrite || op.isWrite()
		txn.sus = append(txn.sus, op.toRequestOp())
	}

	return txn
}

func (txn *txn) Else(ops ...Op) Txn {
	txn.mu.Lock()
	defer txn.mu.Unlock()

	if txn.celse {
		panic("cannot call Else twice!")

View on GitHub (pinned to f744d457f4)

Solutions

  1. Reorder so Then(...) precedes Else(...) on the Txn
  2. Assemble thenOps and elseOps slices locally, then chain once: If(cs...).Then(thenOps...).Else(elseOps...)
  3. Do not mutate a Txn across helpers; build it in exactly one function
  4. Cover composite transactions with unit tests to catch chain-order regressions

Example fix

// before
t := cli.Txn(ctx).If(c).Else(clientv3.OpGet("k"))
t = t.Then(clientv3.OpPut("k", "v")) // panics

// after
t := cli.Txn(ctx).If(c).
	Then(clientv3.OpPut("k", "v")).
	Else(clientv3.OpGet("k"))
Defensive patterns

Strategy: validation

Validate before calling

// Chain in canonical order, assembled from slices:
t := cli.Txn(ctx).If(cmps...).Then(thenOps...).Else(elseOps...)

Prevention

When it happens

Trigger: Sequences like cli.Txn(ctx).Then(a).Else(b).Then(c), or cli.Txn(ctx).Else(b).Then(a) — any Then after an Else. Typically caused by reordered builder chains in refactored code or helpers appending then-ops after the else-branch was already supplied.

Common situations: Wrappers that let callers register then/else callbacks in arbitrary order; merges where a Then block got appended after the Else block; copy-paste insertion at the wrong position in a chain.

Related errors


AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15). Data as JSON: /api/errors/0556eca176c16b5a. Report an issue: GitHub.