Search code examples
gogo-templatesgo-html-template

Why doesn't template.ParseFiles() detect this error?


If I specify a non-existent template in my template file, the error is not detected by ParseFiles() but by ExecuteTemplate(). One would expect parsing to detect any missing templates. Detecting such errors during parsing could also lead to performance improvements.

{{define "test"}}
<html>
    <head>
        <title> test </title>
    </head>
    <body>
        <h1> Hello, world!</h1>
        {{template "doesnotexist"}}
    </body>
</html>
{{end}}

main.go

package main

import (
    "html/template"
    "os"
    "fmt"
)

func main() {
    t, err := template.ParseFiles("test.html")
    if err != nil {
        fmt.Printf("ParseFiles: %s\n", err)
        return
    }

    err = t.ExecuteTemplate(os.Stdout, "test", nil)
    if err != nil {
        fmt.Printf("ExecuteTemplate: %s\n", err)
    }
}

10:46:30 $ go run main.go 
ExecuteTemplate: html/template:test.html:8:19: no such template "doesnotexist"
10:46:31 $ 

Solution

  • template.ParseFiles() doesn't report missing templates, because often not all the templates are parsed in a single step, and reporting missing templates (by template.ParseFiles()) would not allow this.

    It's possible to parse templates using multiple calls, from multiple sources.

    For example if you call the Template.Parse() method or your template, you can add more templates to it:

    _, err = t.Parse(`{{define "doesnotexist"}}the missing piece{{end}}`)
    if err != nil {
        fmt.Printf("Parse failed: %v", err)
        return
    }
    

    The above code will add the missing piece, and your template execution will succeed and generate the output (try it on the Go Playground):

    <html>
        <head>
            <title> test </title>
        </head>
        <body>
            <h1> Hello, world!</h1>
            the missing piece
        </body>
    </html>
    

    Going further, not requiring all templates to be parsed and "presented" gives you optimization possibilities. It's possible there are admin pages which are never used by "normal" users, and are only required if an admin user starts or uses your app. In that case you can speed up startup and same memory by not having to parse admin pages (only when / if an admin user uses your app).

    See related: Go template name