ethereum/go-ethereum · critical
%T: invalid node: %v
Error message
%T: invalid node: %v
What it means
LevelStats.add panics when asked to account for a node that is not a *shortNode, *fullNode, or valueNode. The per-level statistics structure has buckets only for those three node kinds; any other node type (or a typed nil in an unexpected interface shape) violates the trie's node model.
Source
Thrown at trie/levelstats.go:83
var total uint64
for i := range s.level {
total += s.level[i].short.Load() + s.level[i].full.Load() + s.level[i].value.Load()
}
return total
}
// add increases the node count by one for the specified node type and depth.
func (s *LevelStats) add(n node, depth uint32) {
d := int(depth)
switch (n).(type) {
case *shortNode:
s.level[d].short.Add(1)
case *fullNode:
s.level[d].full.Add(1)
case valueNode:
s.level[d].value.Add(1)
default:
panic(fmt.Sprintf("%T: invalid node: %v", n, n))
}
}
// addSize increases the raw byte-size tally at the specified depth.
func (s *LevelStats) addSize(depth uint32, size uint64) {
s.level[depth].size.Add(size)
}
// AddLeaf records a leaf depth. Witness collection reuses the value-node bucket
// for leaf accounting. It panics if the depth is outside [0, 15].
func (s *LevelStats) AddLeaf(depth int) {
s.level[depth].value.Add(1)
}
// LeafDepths returns leaf counts grouped by depth.
func (s *LevelStats) LeafDepths() [trieStatLevels]int64 {
var leaves [trieStatLevels]int64
for i := range s.level {View on GitHub (pinned to 6bb0588ad8)
Solutions
- If you extended node types, add a case (or explicit skip) for them in LevelStats.add.
- Ensure the trie is not being mutated while statistics are collected.
- Validate/recover the trie data source before running statistics.
- Report with the %T value from the panic message to identify the offending type.
Defensive patterns
Strategy: try-catch
Try / catch
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("stats collection hit invalid node: %v", r)
}
}()
stat := trie.NewLevelStats() // ...
_ = stat Prevention
- Freeze the trie (no writes) while collecting statistics.
- Extend the switch in LevelStats.add if your fork introduces node types.
- Recover at tool boundaries so one bad subtree does not kill a whole dump run.
When it happens
Trigger: Feeding hashNode or custom node implementations into LevelStats.add; statistics collection running over corrupted trie memory; forks adding node types without extending the stats switch.
Common situations: Trie dump/size-analysis tooling over damaged data; concurrent mutation racing the statistics pass; version drift where a new node type exists but levelstats.go was not updated.
Related errors
AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15).
Data as JSON: /api/errors/d72a5ff9d17bdb16.
Report an issue: GitHub.