{"record":{"id":"d72a5ff9d17bdb16","repo":"ethereum/go-ethereum","slug":"t-invalid-node-v-d72a5f","errorCode":null,"errorMessage":"%T: invalid node: %v","messagePattern":"%T: invalid node: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"trie/levelstats.go","lineNumber":83,"sourceCode":"\tvar total uint64\n\tfor i := range s.level {\n\t\ttotal += s.level[i].short.Load() + s.level[i].full.Load() + s.level[i].value.Load()\n\t}\n\treturn total\n}\n\n// add increases the node count by one for the specified node type and depth.\nfunc (s *LevelStats) add(n node, depth uint32) {\n\td := int(depth)\n\tswitch (n).(type) {\n\tcase *shortNode:\n\t\ts.level[d].short.Add(1)\n\tcase *fullNode:\n\t\ts.level[d].full.Add(1)\n\tcase valueNode:\n\t\ts.level[d].value.Add(1)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"%T: invalid node: %v\", n, n))\n\t}\n}\n\n// addSize increases the raw byte-size tally at the specified depth.\nfunc (s *LevelStats) addSize(depth uint32, size uint64) {\n\ts.level[depth].size.Add(size)\n}\n\n// AddLeaf records a leaf depth. Witness collection reuses the value-node bucket\n// for leaf accounting. It panics if the depth is outside [0, 15].\nfunc (s *LevelStats) AddLeaf(depth int) {\n\ts.level[depth].value.Add(1)\n}\n\n// LeafDepths returns leaf counts grouped by depth.\nfunc (s *LevelStats) LeafDepths() [trieStatLevels]int64 {\n\tvar leaves [trieStatLevels]int64\n\tfor i := range s.level {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/trie/levelstats.go#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"stats collection hit invalid node: %v\", r)\n    }\n}()\nstat := trie.NewLevelStats() // ...\n_ = stat","preventionTips":["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."],"tags":["trie","statistics","invariant-violation","internal","corruption","panic","go"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}