ethereum/go-ethereum · critical
node %x: %v
Error message
node %x: %v
What it means
mustDecodeNode panics when decodeNode fails to RLP-decode a trie node blob, embedding the node hash and the decode error in the message. It is used on the hot path where node bytes are trusted; a decode failure means the stored bytes for that hash are not a valid trie node — typically database corruption or wrong-network data.
Source
Thrown at trie/node.go:130
}
return resp + fmt.Sprintf("\n%s] ", ind)
}
func (n *shortNode) fstring(ind string) string {
return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" "))
}
func (n hashNode) fstring(ind string) string {
return fmt.Sprintf("<%x> ", []byte(n))
}
func (n valueNode) fstring(ind string) string {
return fmt.Sprintf("%x ", []byte(n))
}
// mustDecodeNode is a wrapper of decodeNode and panic if any error is encountered.
func mustDecodeNode(hash, buf []byte) node {
n, err := decodeNode(hash, buf)
if err != nil {
panic(fmt.Sprintf("node %x: %v", hash, err))
}
return n
}
// mustDecodeNodeUnsafe is a wrapper of decodeNodeUnsafe and panic if any error is
// encountered.
func mustDecodeNodeUnsafe(hash, buf []byte) node {
n, err := decodeNodeUnsafe(hash, buf)
if err != nil {
panic(fmt.Sprintf("node %x: %v", hash, err))
}
return n
}
// decodeNode parses the RLP encoding of a trie node. It will deep-copy the passed
// byte slice for decoding, so it's safe to modify the byte slice afterwards. The-
// decode performance of this function is not optimal, but it is suitable for most
// scenarios with low performance requirements and hard to determine whether theView on GitHub (pinned to 6bb0588ad8)
Solutions
- Run the node's database integrity checks and inspect the reported node hash in the logs.
- If corruption is confirmed, resync the chain data (or restore the datadir from a good backup/archive).
- Ensure all writers/readers of the database run the same (compatible) software version.
- Check disk health (dmesg/SMART) if corruption appears spontaneous.
Defensive patterns
Strategy: try-catch
Try / catch
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("trie node decode failed: %v", r) // message contains node hash
}
}()
tr, err := trie.New(root, db) Prevention
- Convert panics to errors with recover at API boundaries; the panic text contains the failing node hash for diagnosis.
- Keep datadir on reliable storage and stop the node cleanly to avoid partial node writes.
- Never mix databases between incompatible software versions; resync when node encodings change.
When it happens
Trigger: Loading a trie node from the database whose stored blob is malformed, truncated, or belongs to a different encoding scheme; opening a database written by incompatible software; disk-level corruption or partial writes.
Common situations: Crash during a database write leaving partial nodes; mixing chain data from different node versions or forks; corrupted datadir after disk failure; restoring from a bad backup; snapshot files that do not match the trie root.
Related errors
- unexpected node type, %T
- %T: invalid node: %v
- %T: invalid node: %v
- invalid group depth size
- not implemented
AI-assisted analysis of ethereum/go-ethereum@6bb0588ad8 (2026-08-15).
Data as JSON: /api/errors/d150904a52e6ffb9.
Report an issue: GitHub.