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
- Pass at least one existing filename to ParseFiles.
- Guard the call site: only invoke ParseFiles when len(files) > 0, otherwise return a descriptive error.
- 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
- Assert the file slice is non-empty before calling ParseFiles.
- Log how many files were discovered by the upstream glob/walk so an empty result is visible.
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
- malformed character constant: %s
- integer overflow: %q
- illegal number syntax: %q
- template: %s:%d: %s
- can't divide the value by 0
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/4dd7486211d4d77c.
Report an issue: GitHub.