gohugoio/hugo · error

template: no files named in call to ParseFiles

Error message

template: no files named in call to ParseFiles

What it means

Returned by template.ParseFiles (function and method form) when the variadic filenames slice is empty. Hugo's vendored Go text/template requires at least one file and treats a zero-length call as a programmer error rather than silently returning an empty template. The guard lives in parseFiles at helper.go:63-66.

Source

Thrown at tpl/internal/go_templates/texttemplate/helper.go:65

// Since the templates created by ParseFiles are named by the base
// (see [filepath.Base]) names of the argument files, t should usually have the
// name of one of the (base) names of the files. If it does not, depending on
// t's contents before calling ParseFiles, t.Execute may fail. In that
// case use t.ExecuteTemplate to execute a valid template.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
	t.init()
	return parseFiles(t, readFileOS, filenames...)
}

// parseFiles is the helper for the method and function. If the argument
// template is nil, it is created from the first file.
func parseFiles(t *Template, readFile func(string) (string, []byte, error), filenames ...string) (*Template, error) {
	if len(filenames) == 0 {
		// Not really a problem, but be consistent.
		return nil, fmt.Errorf("template: no files named in call to ParseFiles")
	}
	for _, filename := range filenames {
		name, b, err := readFile(filename)
		if err != nil {
			return nil, err
		}
		s := string(b)
		// First template becomes return value if not already defined,
		// and we use that one for subsequent New calls to associate
		// all the templates together. Also, if this file has the same name
		// as t, this file becomes the contents of t, so
		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
		// works. Otherwise we create a new template associated with t.
		var tmpl *Template
		if t == nil {
			t = New(name)
		}
		if name == t.Name() {

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Pass at least one existing filename to ParseFiles.
  2. Guard the call site: only invoke ParseFiles when len(files) > 0, otherwise return a descriptive error.
  3. Trace upstream: verify the slice population logic (glob results, config reads, directory walks) is not returning empty unexpectedly.

Example fix

// before
t, err := template.New("x").ParseFiles(files...)

// after
if len(files) == 0 {
    return fmt.Errorf("no template files configured")
}
t, err := template.New("x").ParseFiles(files...)
Defensive patterns

Strategy: validation

Validate before calling

if len(filenames) == 0 {
    return nil, fmt.Errorf("no template files to parse")
}
t, err := template.New("x").ParseFiles(filenames...)

Try / catch

t, err := template.ParseFiles(files...)
if err != nil {
    return fmt.Errorf("loading templates: %w", err)
}

Prevention

When it happens

Trigger: Calling template.New("x").ParseFiles() with no arguments, or passing a computed []string that happens to be empty, e.g. ParseFiles(files...) where files was populated by a filter/glob that returned nothing.

Common situations: Dynamically building the file list from config or globbing and the source yields no matches; refactoring that drops the filename argument; unit tests that stub file discovery to return an empty slice.

Related errors


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