{"record":{"id":"ca94788aa8031635","repo":"gohugoio/hugo","slug":"html-template-cannot-parse-after-execute","errorCode":null,"errorMessage":"html/template: cannot Parse after Execute","messagePattern":"html/template: cannot Parse after Execute","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/internal/go_templates/htmltemplate/template.go","lineNumber":91,"sourceCode":"//\t\"missingkey=zero\"\n//\t\tThe operation returns the zero value for the map type's element.\n//\t\"missingkey=error\"\n//\t\tExecution stops immediately with an error.\nfunc (t *Template) Option(opt ...string) *Template {\n\tt.text.Option(opt...)\n\treturn t\n}\n\n// checkCanParse checks whether it is OK to parse templates.\n// If not, it returns an error.\nfunc (t *Template) checkCanParse() error {\n\tif t == nil {\n\t\treturn nil\n\t}\n\tt.nameSpace.mu.Lock()\n\tdefer t.nameSpace.mu.Unlock()\n\tif t.nameSpace.escaped {\n\t\treturn fmt.Errorf(\"html/template: cannot Parse after Execute\")\n\t}\n\treturn nil\n}\n\n// escape escapes all associated templates.\nfunc (t *Template) escape() error {\n\tt.nameSpace.mu.Lock()\n\tdefer t.nameSpace.mu.Unlock()\n\tt.nameSpace.escaped = true\n\tif t.escapeErr == nil {\n\t\tif t.Tree == nil {\n\t\t\treturn fmt.Errorf(\"template: %q is an incomplete or empty template\", t.Name())\n\t\t}\n\t\tif err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if t.escapeErr != escapeOK {\n\t\treturn t.escapeErr","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/internal/go_templates/htmltemplate/template.go#L73-L109","documentation":"Returned by checkCanParse, which guards Parse, AddParseTree, ParseFiles, ParseGlob and ParseFS. Once a template's nameSpace.escaped flag is true (set by escape() on the first Execute), adding or redefining templates is forbidden because html/template applies context-aware escaping to the complete set and cannot safely escape newly added nodes afterward.","triggerScenarios":"Any call to t.Parse, t.AddParseTree, t.ParseFiles, t.ParseGlob, or t.ParseFS (and the package-level ParseFiles/ParseGlob/ParseFS) on an html/template Template whose nameSpace.escaped is already true from a prior Execute/ExecuteTemplate.","commonSituations":"Hot-reloading or live-parsing templates into a set that was already rendered; calling Execute during setup (e.g. to warm caches) then Parse to add more partials; migrating from text/template (which allows post-execute parse) to html/template.","solutions":["Finish all Parse/ParseFiles/ParseGlob/ParseFS calls before the first Execute on the template or any associated template.","If you need to add templates after rendering, Clone the original before executing and add templates to the clone.","Restructure so parsing is a one-time init phase separated from the render phase.","Use a fresh New(name) template set for each render cycle instead of mutating an executed one."],"exampleFix":"// before\nt.Execute(w, data)\n_, err := t.Parse(`{{define \"x\"}}new{{end}}`) // error\n\n// after\n_, err := t.Parse(`{{define \"x\"}}new{{end}}`) // parse first\nif err != nil { return err }\nt.Execute(w, data)","handlingStrategy":"validation","validationCode":"// Enforce parse-before-execute ordering at the call site.\nparsed, executed := false, false\nfunc parseThenExec(t *template.Template, body string, w io.Writer, data any) error {\n    if executed {\n        return fmt.Errorf(\"cannot Parse after Execute; rebuild the set\")\n    }\n    if _, err := t.Parse(body); err != nil { return err }\n    parsed = true\n    executed = true\n    return t.Execute(w, data)\n}","typeGuard":null,"tryCatchPattern":"if _, err := t.Parse(body); err != nil {\n    if strings.Contains(err.Error(), \"cannot Parse after Execute\") {\n        // start a fresh set instead of mutating the executed one\n        t = template.New(t.Name())\n        _, err = t.Parse(body)\n    }\n    if err != nil { return err }\n}","preventionTips":["Segregate phases: parse everything first, then execute.","For dynamic additions, clone-then-parse rather than parse-into-executed.","Add a lint/test asserting no Parse call follows an Execute on the same set."],"tags":["go","html-template","parse","escaping"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}