gohugoio/hugo · error
html/template: cannot Clone %q after it has executed
Error message
html/template: cannot Clone %q after it has executed
What it means
Thrown by html/template CloneShallow (Hugo's shallow-clone helper) when the template has already been escaped/executed. html/template freezes its escaping state after the first Execute (escapeErr becomes non-nil), and cloning an already-escaped template would copy an inconsistent escaping namespace, so CloneShallow refuses. The %q is the offending template's Name().
Source
Thrown at tpl/internal/go_templates/htmltemplate/hugo_template.go:76
}
func indirect(a any) any {
in := doIndirect(a)
// We have a special Result type that we want to unwrap when printed.
if pp, ok := in.(types.PrintableValueProvider); ok {
return pp.PrintableValue()
}
return in
}
// CloneShallow creates a shallow copy of the template. It does not clone or copy the nested templates.
func (t *Template) CloneShallow() (*Template, error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
if t.escapeErr != nil {
return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
}
textClone, err := t.text.Clone()
if err != nil {
return nil, err
}
ns := &nameSpace{set: make(map[string]*Template)}
ns.esc = makeEscaper(ns)
ret := &Template{
nil,
textClone,
textClone.Tree,
ns,
}
ret.set[ret.Name()] = ret
// Return the template associated with the name of this template.
return ret.set[ret.Name()], nil
}View on GitHub (pinned to 52c9bd7908)
Solutions
- Call CloneShallow before the first Execute/ExecuteTemplate/Prepare on the template or any of its associated siblings.
- If you must branch after rendering, rebuild the template from scratch with New(name) + Parse instead of cloning.
- Clone once at startup to produce the base variant, then Execute the clones (each clone gets its own escaping state).
- Audit call sites to ensure no shared template is both executed and later shallow-cloned.
Example fix
// before
t.Execute(os.Stdout, data)
clone, err := t.CloneShallow() // error: already executed
// after
clone, err := t.CloneShallow() // clone first
if err != nil { return err }
t.Execute(os.Stdout, data) // execute original (or the clone) Defensive patterns
Strategy: validation
Validate before calling
// Clone before any Execute. Track executed state explicitly.
var executed bool
func render(t *template.Template, w io.Writer, data any) error {
if !executed {
base, err := t.CloneShallow()
if err != nil {
return fmt.Errorf("clone-before-execute: %w", err)
}
_ = base
}
executed = true
return t.Execute(w, data)
} Try / catch
clone, err := t.CloneShallow()
if err != nil {
// rebuild from scratch instead of cloning an executed template
t = template.New(t.Name())
_, err = t.Parse(originalBody)
if err != nil { return err }
clone, err = t.CloneShallow()
} Prevention
- Treat an html/template instance as clone-once: clone before the first render.
- Keep a dedicated unexecuted master template for cloning.
- Never Execute and CloneShallow on the same instance lifecycle.
When it happens
Trigger: Calling (*htmltemplate.Template).CloneShallow() on a Template whose escapeErr field is set (non-nil and not the escapeOK sentinel). escapeErr is set the moment escape() runs during Execute/ExecuteTemplate/Prepare on the template or any template sharing its nameSpace.
Common situations: Hugo rebuilds that try to reuse and clone an already-rendered template set; a long-running server that renders once then tries to clone the same instance for per-request variants; helper code that assumes a template can be cloned repeatedly.
Related errors
- html/template: cannot Clone %q after it has executed
- html/template: cannot Parse after Execute
- deploy not supported in this version of Hugo; install a rele
- config not set
- target path "{targetDir}" exists but is not a directory
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/24802d9db1547bbc.
Report an issue: GitHub.