{"id":"0213c2e43ce1b9b3","repo":"gohugoio/hugo","slug":"resources-postprocess-cannot-be-used-in-a-deferred","errorCode":null,"errorMessage":"resources.PostProcess cannot be used in a deferred template","messagePattern":"resources\\.PostProcess cannot be used in a deferred template","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/tplimpl/templatetransform.go","lineNumber":453,"sourceCode":"\t\treturn\n\t}\n\n\tidArg = cmd.Args[0]\n\n\tif !c.isWithDefer(idArg) {\n\t\treturn\n\t}\n\n\tdeferArg := cmd.Args[1]\n\tcmd.Args = []parse.Node{idArg}\n\n\tl := doDefer.CopyList()\n\tn := l.Nodes[0].(*parse.ActionNode)\n\n\tinner := withNode.List.CopyList()\n\ts := inner.String()\n\tif strings.Contains(s, \"resources.PostProcess\") {\n\t\tc.err = errors.New(\"resources.PostProcess cannot be used in a deferred template\")\n\t\treturn\n\t}\n\tinnerHash := hashing.XxHashFromStringHexEncoded(s)\n\tdeferredID := tpl.HugoDeferredTemplatePrefix + innerHash\n\n\tc.deferNodes[deferredID] = inner\n\twithNode.List = l\n\n\tn.Pipe.Cmds[0].Args[1].(*parse.PipeNode).Cmds[0].Args[0].(*parse.StringNode).Text = deferredID\n\tn.Pipe.Cmds[0].Args[2] = deferArg\n}\n\nfunc (c *templateTransformContext) applyTransformationsToNodes(nodes ...parse.Node) {\n\tfor _, node := range nodes {\n\t\tc.applyTransformations(node)\n\t}\n}\n","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/tplimpl/templatetransform.go#L435-L471","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"{{/* before */}}\n{{ with (templates.Defer (dict \"key\" \"css\")) }}\n  {{ $css := resources.Get \"css/main.css\" | css.TailwindCSS | resources.PostProcess }}\n{{ end }}\n\n{{/* after */}}\n{{ with (templates.Defer (dict \"key\" \"css\")) }}\n  {{ $css := resources.Get \"css/main.css\" | css.TailwindCSS | minify | fingerprint }}\n  <link rel=\"stylesheet\" href=\"{{ $css.RelPermalink }}\">\n{{ end }}","handlingStrategy":"validation","validationCode":"{{/* Keep PostProcess out of templates.Defer bodies */}}\n{{ with (templates.Defer (dict \"key\" \"k\")) }}\n  {{/* no resources.PostProcess here */}}\n{{ end }}\n{{/* Do PostProcess in the normal render path instead */}}\n{{ $css := resources.Get \"css/main.css\" | postCSS | resources.PostProcess }}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["hugo","templates","defer","resources","postprocess"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}