gohugoio/hugo · critical
Handle cannot replace nodes in the tree, use Transform for t
Error message
Handle cannot replace nodes in the tree, use Transform for that
What it means
A panic in NodeShiftTreeWalker.Walk (hugolib/doctree/nodeshifttree.go:596) when the Handle callback returns a WalkFlag with the Set bit (radix.WalkSet). Replacing nodes is the exclusive job of Transform; Handle is read-only with respect to node replacement.
Source
Thrown at hugolib/doctree/nodeshifttree.go:596
ns = NodeTransformStateSkip
}
return
}()
if ns == NodeTransformStateTerminate || err != nil {
return radix.WalkStop, zero, err
}
if ns == NodeTransformStateSkip {
return radix.WalkContinue, zero, nil
}
}
if r.Handle != nil {
f, err := r.Handle(s, t)
if f.ShouldSet() {
panic("Handle cannot replace nodes in the tree, use Transform for that")
}
if f.ShouldStop() || err != nil {
return f, zero, err
}
}
if ns == NodeTransformStateTerminate {
return radix.WalkStop, zero, nil
}
if ns == NodeTransformStateReplaced {
return radix.WalkSet | radix.WalkContinue, t2, nil
}
return radix.WalkContinue, zero, nil
}
handle := &walkHandler[T]{
r: r,
handle: handleV,
}View on GitHub (pinned to 52c9bd7908)
Solutions
- Move node-replacement logic into Transform and set NoShift=true
- Have Handle return only WalkContinue/WalkStop without WalkSet
Example fix
// before
Handle: func(s string, v contentNode) (radix.WalkFlag, error) {
return radix.WalkSet | radix.WalkContinue, nil
}
// after
Handle: func(s string, v contentNode) (radix.WalkFlag, error) {
return radix.WalkContinue, nil
} Defensive patterns
Strategy: validation
Validate before calling
// Inside a Handle callback, never return WalkSet. return radix.WalkContinue, nil
Prevention
- Keep Handle callbacks read-only (WalkContinue/WalkStop only)
- Use Transform (with NoShift=true) when node replacement is needed
- Audit WalkFn return values copied between Transform and Handle
When it happens
Trigger: A Handle callback returns radix.WalkSet | radix.WalkContinue, attempting to replace the current node.
Common situations: Copying a WalkFn that returns WalkSet into Handle instead of Transform; misunderstanding the Handle/Transform split.
Related errors
- Transform must be performed with NoShift=true
- Shifter is required
- transformerRaw is required
- DeleteFunc: unknown type %T
- Insert: unknown type %T
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/e28686b0f03c29c2.
Report an issue: GitHub.