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

  1. If you forked or extended node types, update hasher.go's switch to handle the new type.
  2. Audit for concurrent access to a single trie instance; use one trie per goroutine or Copy() per goroutine.
  3. Rebuild/re-sync the trie from the database to discard in-memory corruption.
  4. 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

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.