gohugoio/hugo · error

resources.PostProcess cannot be used in a deferred template

Error message

resources.PostProcess cannot be used in a deferred template

What it means

templates.Defer extracts the with-block body into a template executed after the main build pass. resources.PostProcess relies on placeholder substitution during normal output rendering, which has already happened by the time deferred templates run, so combining them would silently emit unresolved placeholders; Hugo rejects it at transform time instead.

Source

Thrown at tpl/tplimpl/templatetransform.go:453

		return
	}

	idArg = cmd.Args[0]

	if !c.isWithDefer(idArg) {
		return
	}

	deferArg := cmd.Args[1]
	cmd.Args = []parse.Node{idArg}

	l := doDefer.CopyList()
	n := l.Nodes[0].(*parse.ActionNode)

	inner := withNode.List.CopyList()
	s := inner.String()
	if strings.Contains(s, "resources.PostProcess") {
		c.err = errors.New("resources.PostProcess cannot be used in a deferred template")
		return
	}
	innerHash := hashing.XxHashFromStringHexEncoded(s)
	deferredID := tpl.HugoDeferredTemplatePrefix + innerHash

	c.deferNodes[deferredID] = inner
	withNode.List = l

	n.Pipe.Cmds[0].Args[1].(*parse.PipeNode).Cmds[0].Args[0].(*parse.StringNode).Text = deferredID
	n.Pipe.Cmds[0].Args[2] = deferArg
}

func (c *templateTransformContext) applyTransformationsToNodes(nodes ...parse.Node) {
	for _, node := range nodes {
		c.applyTransformations(node)
	}
}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Remove resources.PostProcess from inside the templates.Defer block; keep fingerprint/minify inside and do PostProcess outside, or drop PostProcess entirely.
  2. Restructure so the resource pipeline needing PostProcess runs in the normal (non-deferred) template flow.
  3. If the string appears only in a template comment inside the block, delete the comment — the check is a plain substring match.

Example fix

{{/* before */}}
{{ with (templates.Defer (dict "key" "css")) }}
  {{ $css := resources.Get "css/main.css" | css.TailwindCSS | resources.PostProcess }}
{{ end }}

{{/* after */}}
{{ with (templates.Defer (dict "key" "css")) }}
  {{ $css := resources.Get "css/main.css" | css.TailwindCSS | minify | fingerprint }}
  <link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{ end }}
Defensive patterns

Strategy: validation

Validate before calling

{{/* Keep PostProcess out of templates.Defer bodies */}}
{{ with (templates.Defer (dict "key" "k")) }}
  {{/* no resources.PostProcess here */}}
{{ end }}
{{/* Do PostProcess in the normal render path instead */}}
{{ $css := resources.Get "css/main.css" | postCSS | resources.PostProcess }}

Prevention

When it happens

Trigger: The string 'resources.PostProcess' appears anywhere inside the body of a {{ with (templates.Defer ...) }} block — the check is a literal substring match on the copied node list, so even a commented or indirect occurrence trips it.

Common situations: Common with Tailwind/PostCSS setups: the documented pattern wraps css.TailwindCSS in templates.Defer, and users add | resources.PostProcess (used for fingerprinting/minify integration in production) inside that same block.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/0213c2e43ce1b9b3.json. Report an issue: GitHub ↗.