tailscale/tailscale · error

computing ancestor: %v

Error message

computing ancestor: %v

What it means

Wraps a failure of computeActiveAncestor, which selects the oldest surviving ancestor shared by the candidate chains. It fires when no single ancestor can be determined — e.g. the candidate chains are entirely distinct or the selection logic hit an internal storage error — so the active chain cannot be pinned to a starting point.

Source

Thrown at tka/tka.go:398

//     formerly (in a previous run) part of the chain.
//  3. Compute the state of the state machine at this ancestor. This is
//     needed for fast-forward, as each update operates on the state of
//     the update preceding it.
//  4. Iteratively apply updates till we reach head ('fast forward').
func computeActiveChain(storage Chonk, lastKnownOldest *AUMHash, maxIter int) (chain, error) {
	chains, err := computeChainCandidates(storage, lastKnownOldest, maxIter)
	if err != nil {
		return chain{}, fmt.Errorf("computing candidates: %v", err)
	}

	if len(chains) == 0 {
		return chain{}, errors.New("no chain candidates in AUM storage")
	}

	// Find the right ancestor.
	oldestHash, err := computeActiveAncestor(chains)
	if err != nil {
		return chain{}, fmt.Errorf("computing ancestor: %v", err)
	}
	ancestor, err := storage.AUM(oldestHash)
	if err != nil {
		return chain{}, err
	}

	// At this stage we know the ancestor AUM, so we have excluded distinct
	// chains but we might still have forks (so we don't know the head AUM).
	//
	// We iterate forward from the ancestor AUM, handling any forks as we go
	// till we arrive at a head.
	out := chain{Oldest: ancestor, Head: ancestor}
	if out.state, err = computeStateAt(storage, maxIter, oldestHash); err != nil {
		return chain{}, fmt.Errorf("bootstrapping state: %v", err)
	}
	out.Head, out.state, err = fastForward(storage, maxIter, out.state, nil)
	if err != nil {
		return chain{}, fmt.Errorf("fast forward: %v", err)

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Examine the candidate chains: fully disjoint chains usually mean bad forks were stored; use MakeRetroactiveRevocation or resync to recover
  2. Ensure lastKnownOldest passed to computeActiveChain is consistent with stored history
  3. Resync from a healthy peer if local history has diverged irrecoverably
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at tka/tka.go:398 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18). Data as JSON: /api/errors/320435ff460fb26f. Report an issue: GitHub.