ethereum/go-ethereum · critical
unexpected node type, %T
Error message
unexpected node type, %T
What it means
The Merkle-Patricia-trie hasher panics on a node type outside its switch (shortNode, fullNode, valueNode, hashNode are handled; anything else hits default). This is an internal invariant violation: the trie node graph only ever contains those four types, so reaching default means memory corruption, a hand-constructed node, or an internal bug.
Source
Thrown at trie/hasher.go:101
if len(enc) < 32 && !force {
// Nodes smaller than 32 bytes are embedded directly in their parent.
// In such cases, return the raw encoded blob instead of the node hash.
// It's essential to deep-copy the node blob, as the underlying buffer
// of enc will be reused later.
buf := make([]byte, len(enc))
copy(buf, enc)
return buf
}
hash := h.hashData(enc)
n.flags.hash = hash
return hash
case hashNode:
// hash nodes don't have children, so they're left as were
return n
default:
panic(fmt.Errorf("unexpected node type, %T", n))
}
}
// encodeShortNode encodes the provided shortNode into the bytes. Notably, the
// return slice must be deep-copied explicitly, otherwise the underlying slice
// will be reused later.
func (h *hasher) encodeShortNode(n *shortNode) []byte {
// Encode leaf node
if hasTerm(n.Key) {
var ln leafNodeEncoder
ln.Key = hexToCompact(n.Key)
ln.Val = n.Val.(valueNode)
ln.encode(h.encbuf)
return h.encodedBytes()
}
// Encode extension node
var en extNodeEncoder
en.Key = hexToCompact(n.Key)View on GitHub (pinned to 6bb0588ad8)
Solutions
- If you forked or extended node types, update hasher.go's switch to handle the new type.
- Audit for concurrent access to a single trie instance; use one trie per goroutine or Copy() per goroutine.
- Rebuild/re-sync the trie from the database to discard in-memory corruption.
- Report upstream with a stack trace if it occurs on unmodified code.
Defensive patterns
Strategy: try-catch
Try / catch
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("trie hashing invariant violated: %v", r)
}
}()
root := t.Hash() // convert panic into error at your API boundary Prevention
- Never mutate or share a trie across goroutines; give each goroutine its own trie or a Copy().
- If you fork the trie and add node types, update every type switch (hasher, stats, inspector).
- Run with -race during development to catch concurrent trie access early.
When it happens
Trigger: Inserting a custom type satisfying the node interface into trie internals; a corrupted or race-mutated trie structure passed to hashing; a fork/modified trie package introducing a new node type without updating the hasher.
Common situations: Running a patched or forked trie with new node kinds; data races mutating trie nodes concurrently (unsynchronized concurrent trie writes); memory corruption from unsafe code; version skew between trie subpackages compiled together.
Related errors
AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15).
Data as JSON: /api/errors/531700aace7efd0f.
Report an issue: GitHub.