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
- Read the %T in the panic message: *diffLayer means flattening was skipped; something else means the stack holds a foreign layer type
- Ensure all triedb reset/truncate/commit operations hold the pathdb lock and cannot interleave
- Don't call diffToDisk from fork code; drive persistence via the public Commit/Checkpoint APIs which maintain ordering
- 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
- Use only public Reset/Commit/Checkpoint entry points so layer ordering holds
- Hold the pathdb lock across any sequence of truncate+commit
- On panic, dump the layer chain (types and heights) before restarting
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.