ethereum/go-ethereum · critical

unknown layer type: %T

Error message

unknown layer type: %T

What it means

diffToDisk merges a bottom-most in-memory diffLayer into the diskLayer beneath it; it requires layer.parentLayer() to be a *diskLayer. If the parent is still another diffLayer (or something else entirely), the layer stack is inconsistent — flattening should have collapsed intermediate diffs first — so the code panics with the actual parent type in the message.

Source

Thrown at triedb/pathdb/difflayer.go:189

			return nil, err
		}
		dl.parent = result
		dl.lock.Unlock()
	}
	return diffToDisk(dl, force)
}

// size returns the approximate memory size occupied by this diff layer.
func (dl *diffLayer) size() uint64 {
	return dl.nodes.size + dl.states.size
}

// diffToDisk merges a bottom-most diff into the persistent disk layer underneath
// it. The method will panic if called onto a non-bottom-most diff layer.
func diffToDisk(layer *diffLayer, force bool) (*diskLayer, error) {
	disk, ok := layer.parentLayer().(*diskLayer)
	if !ok {
		panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer()))
	}
	return disk.commit(layer, force)
}

View on GitHub (pinned to 6bb0588ad8)

Solutions

  1. Read the %T in the panic message: *diffLayer means flattening was skipped; something else means the stack holds a foreign layer type
  2. Ensure all triedb reset/truncate/commit operations hold the pathdb lock and cannot interleave
  3. Don't call diffToDisk from fork code; drive persistence via the public Commit/Checkpoint APIs which maintain ordering
  4. If reproducible on unmodified geth, capture layer heights in the panic and report upstream

Example fix

// before
diffToDisk(dl, true) // panics when dl.parent is another *diffLayer

// after
// only ever persist the bottom-most diff, after flattening
for parent, ok := dl.parentLayer().(*diffLayer); ok; parent, ok = parent.parentLayer().(*diffLayer) {
    dl = parent
}
diffToDisk(dl, true)
Defensive patterns

Strategy: validation

Validate before calling

// Fork code: flatten to the bottom-most diff before persisting
func bottomMost(dl *pathdb.diffLayer) *pathdb.diffLayer {
    for {
        p, ok := dl.parentLayer().(*pathdb.diffLayer)
        if !ok { return dl }
        dl = p
    }
}

Type guard

func parentIsDisk(dl *diffLayer) bool {
    _, ok := dl.parentLayer().(*diskLayer)
    return ok
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("layer stack inconsistent at diffToDisk: %v", r)
    }
}()
_, err = diffToDisk(dl, force)

Prevention

When it happens

Trigger: Calling diffToDisk on a diffLayer that is not bottom-most, i.e. whose parent is another diffLayer. In stock geth this is reached only through internal checkpoint/commit logic; a panic means the layer list got out of order (e.g. truncation while new diffs were added, or concurrent layer mutation).

Common situations: Bugs in pathdb layer truncation (reset/rollback) racing with new block commits; forks calling internal persistence functions directly; history/pruning configuration interactions that skip the flatten step.

Related errors


AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15). Data as JSON: /api/errors/8bbf56ee46129095. Report an issue: GitHub.