gohugoio/hugo · critical

Transform must be performed with NoShift=true

Error message

Transform must be performed with NoShift=true

What it means

A panic in NodeShiftTreeWalker.Walk (hugolib/doctree/nodeshifttree.go:565) when a Transform callback is set but NoShift is false. Transform replaces raw tree nodes; shifting them mid-walk would corrupt iteration, so Transform is only permitted with NoShift=true.

Source

Thrown at hugolib/doctree/nodeshifttree.go:565

			if r.NoShift {
				t = v
			} else {
				var ok bool
				t, ok = r.toT(r.Tree, v)
				if !ok {
					return radix.WalkContinue, zero, nil
				}
			}
			var (
				ns NodeTransformState
				t2 T
			)
			if r.IncludeFilter != nil && !r.IncludeFilter(s, t) {
				return radix.WalkContinue, zero, nil
			}
			if r.Transform != nil {
				if !r.NoShift {
					panic("Transform must be performed with NoShift=true")
				}
				var err error
				ns, err = func() (ns NodeTransformState, err error) {
					t2, ns, err = r.Transform(s, t)
					if ns >= NodeTransformStateSkip || err != nil {
						return
					}
					switch ns {
					case NodeTransformStateReplaced:
					case NodeTransformStateDeleted:
						// Delay delete until after the walk.
						deletes = append(deletes, s)
						ns = NodeTransformStateSkip
					}
					return
				}()

				if ns == NodeTransformStateTerminate || err != nil {

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Set walker.NoShift = true whenever you set walker.Transform
  2. Split into two passes: a shifted read pass, then a NoShift transform pass
  3. If you only need to inspect nodes, use Handle instead of Transform

Example fix

// before
w := &doctree.NodeShiftTreeWalker[contentNode]{
    Tree: tree,
    Transform: transformFn,
}
// after
w := &doctree.NodeShiftTreeWalker[contentNode]{
    Tree: tree,
    Transform: transformFn,
    NoShift: true,
}
Defensive patterns

Strategy: validation

Validate before calling

if walker.Transform != nil && !walker.NoShift {
    return errors.New("set NoShift=true when using Transform")
}

Prevention

When it happens

Trigger: Setting walker.Transform != nil and leaving walker.NoShift at its default (false).

Common situations: Writing a custom walk that mutates nodes but forgetting to set NoShift; adapting a read-only walker into a transform walker.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/451587ffe759560b. Report an issue: GitHub.