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
- Remove resources.PostProcess from inside the templates.Defer block; keep fingerprint/minify inside and do PostProcess outside, or drop PostProcess entirely.
- Restructure so the resource pipeline needing PostProcess runs in the normal (non-deferred) template flow.
- 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
- PostProcess runs after the build writes output; deferred templates render too late for it — keep them separate
- Audit any templates.Defer block for resources.PostProcess before adding it
- Move fingerprinting/purging pipelines to non-deferred partials
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
- templates.Defer cannot be used inside a partialCached partia
- Defer does not take any arguments
- no Key set in Resource
- no Resource provided in transformation
- type %T not supported in Resource transformations
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/0213c2e43ce1b9b3.json.
Report an issue: GitHub ↗.