tailscale/tailscale · error

marking ancestor intersection: %w

Error message

marking ancestor intersection: %w

What it means

Wraps markAncestorIntersectionAUMs: after retention marking, compaction walks each retained AUM's ancestry to guarantee every retained chain reaches the candidate compaction ancestor, moving the candidate earlier when branches rejoin the active chain above it. This error therefore covers the ancestor-read failures ('reading X'), the child-walk failure ('reading children X'), and the integrity error 'reached genesis AUM without intersecting with candidate ancestor' from that phase. Compaction aborts before anything is deleted, so the chonk stays consistent — only larger.

Source

Thrown at tka/tailchonk.go:1034

	if err != nil {
		return AUMHash{}, fmt.Errorf("AllAUMs: %w", err)
	}
	verdict := make(map[AUMHash]retainState, len(all))
	for _, h := range all {
		verdict[h] = 0
	}

	if err := markYoungAUMs(storage, verdict, opts.MinAge); err != nil {
		return AUMHash{}, fmt.Errorf("marking young AUMs: %w", err)
	}
	if lastActiveAncestor, err = markActiveChain(storage, verdict, opts.MinChain, head); err != nil {
		return AUMHash{}, fmt.Errorf("marking active chain: %w", err)
	}
	if err := markDescendantAUMs(storage, verdict); err != nil {
		return AUMHash{}, fmt.Errorf("marking descendant AUMs: %w", err)
	}
	if lastActiveAncestor, err = markAncestorIntersectionAUMs(storage, verdict, lastActiveAncestor); err != nil {
		return AUMHash{}, fmt.Errorf("marking ancestor intersection: %w", err)
	}

	toDelete := make([]AUMHash, 0, len(verdict))
	for h, v := range verdict {
		if v&retainAUMMask == 0 { // no retention set
			toDelete = append(toDelete, h)
		}
	}

	if err := storage.SetLastActiveAncestor(lastActiveAncestor); err != nil {
		return AUMHash{}, err
	}
	return lastActiveAncestor, storage.PurgeAUMs(toDelete)
}

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Read the inner error: 'reading X' means ancestor X is missing or corrupt — fetch it from a peer or quarantine the dangling branch; 'reached genesis' means a retained chain never intersects the candidate — complete the resync before compacting
  2. Verify every retained AUM can walk to a stored ancestor (parents all present in AllAUMs) before retrying Compact
  3. Safe fallback: skip compaction this cycle — nothing is deleted and storage keeps growing until the chain is repairable

Example fix

// before
if _, err := tka.Compact(chonk, head, opts); err != nil {
	return err // marking ancestor intersection: reading 3f9a..: ...
}

// after: compaction is best-effort housekeeping
if _, err := tka.Compact(chonk, head, opts); err != nil {
	log.Printf("tka compaction deferred: %v", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

all, err := storage.AllAUMs()
if err != nil { return err }
have := make(map[tka.AUMHash]bool, len(all))
for _, h := range all { have[h] = true }
for _, h := range all {
	a, err := storage.AUM(h)
	if err != nil { continue }
	if p, ok := a.Parent(); ok && !have[p] {
		return fmt.Errorf("AUM %v references missing parent %v; complete sync before compaction", h, p)
	}
}

Try / catch

if _, err := tka.Compact(storage, head, opts); err != nil {
	// ancestor-intersection walk failed: skip compaction entirely this cycle;
	// nothing was deleted, storage stays consistent (just larger). Retry after repair/resync.
	log.Printf("tka compaction deferred: %v", err)
}

Prevention

When it happens

Trigger: Retained AUMs whose ancestry contains missing (purged or never-synced) or corrupt AUMs; forks that intersect the active chain before the candidate ancestor while their history is incomplete; concurrent writers racing the walk.

Common situations: Compacting a node whose fork sync never completed all history; earlier manual deletion of 'old' files that were still referenced by retained branches.

Related errors


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