gohugoio/hugo · error

html/template: %q is an incomplete template

Error message

html/template: %q is an incomplete template

What it means

Returned by lookupAndEscapeTemplate when the named template IS in the set but its text.Tree or text.Root is nil, meaning it has no parse tree to execute. This differs from "undefined" (663): the template entry exists but has no body. The %q is the requested name.

Source

Thrown at tpl/internal/go_templates/htmltemplate/template.go:158

	return tmpl.text.Execute(wr, data)
}

// lookupAndEscapeTemplate guarantees that the template with the given name
// is escaped, or returns an error if it cannot be. It returns the named
// template.
func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
	t.nameSpace.mu.Lock()
	defer t.nameSpace.mu.Unlock()
	t.nameSpace.escaped = true
	tmpl = t.set[name]
	if tmpl == nil {
		return nil, fmt.Errorf("html/template: %q is undefined", name)
	}
	if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
		return nil, tmpl.escapeErr
	}
	if tmpl.text.Tree == nil || tmpl.text.Root == nil {
		return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
	}
	if t.text.Lookup(name) == nil {
		panic("html/template internal error: template escaping out of sync")
	}
	if tmpl.escapeErr == nil {
		err = escapeTemplate(tmpl, tmpl.text.Root, name)
	}
	return tmpl, err
}

// DefinedTemplates returns a string listing the defined templates,
// prefixed by the string "; defined templates are: ". If there are none,
// it returns the empty string. Used to generate an error message.
func (t *Template) DefinedTemplates() string {
	return t.text.DefinedTemplates()
}

// Parse parses text as a template body for t.

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Provide actual non-empty parsed content for the named template before executing it.
  2. Check t.Lookup(name) and confirm its underlying text template has a non-nil Tree/Root before ExecuteTemplate.
  3. Remove or skip empty placeholder definitions; render a fallback instead.
  4. Audit parse results to ensure every define in the set received a body.

Example fix

// before
t.New("empty")
err := t.ExecuteTemplate(w, "empty", data) // error: incomplete

// after
t.New("empty")
_, err := t.Lookup("empty").Parse(`<p>{{.}}</p>`)
if err != nil { return err }
err = t.ExecuteTemplate(w, "empty", data)
Defensive patterns

Strategy: validation

Validate before calling

func executeComplete(t *template.Template, name string, w io.Writer, data any) error {
    sub := t.Lookup(name)
    if sub == nil || sub.Tree == nil {
        return fmt.Errorf("template %q incomplete; parse a body first", name)
    }
    return t.ExecuteTemplate(w, name, data)
}

Try / catch

if err := t.ExecuteTemplate(w, name, data); err != nil {
    if strings.Contains(err.Error(), "is an incomplete template") {
        if sub := t.Lookup(name); sub != nil {
            if _, perr := sub.Parse(fallbackBody); perr == nil {
                return t.ExecuteTemplate(w, name, data)
            }
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling ExecuteTemplate on a template that was created (e.g. via t.New(name)) and added to the set but never parsed, or whose definition body was only whitespace/comments and got dropped, leaving Tree nil.

Common situations: A {{define "x"}}{{end}} with empty body that was treated as empty; a New(name) reservation never followed by Parse; a partial file that is empty or contains only comments; a template whose Parse failed but the name was already registered.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/7a8ebc641b977447. Report an issue: GitHub.