I'm currently available for contract work & technical writing opportunities. Hire me

How to handle template execution errors in Go

Using the template.Execute() function may produce an error. This snippet describes how to prevent that error from causing a broken page to be rendered to users.

A common problem when dealing with templates in Go is the handling of errors when attempting to render a template. An error may occur in the process of rendering a template when an attempt is made to access data that does not exist, or when a function that was invoked returns an error.

package main

import (
	"html/template"
	"net/http"
)

var tpl = template.Must(template.ParseFiles("index.html"))

func handler(w http.ResponseWriter, r *http.Request) {
	err := tpl.Execute(w, nil)
	if err != nil {
		// How to handle error now that parts of the template
		// have already been written and HTTP status code cannot
		// be changed
	}
}

Once the template starts executing to the ResponseWriter, a 200 status code is implied and it’s not possible to undo what has already been sent even if an error occurs. The effect is that when an error occurs in the template, the execution is halted but the parts preceding the point where the error occurred have already been sent, so users may end up with a half rendered page which neither works nor conveys the necessary error message.

To avoid this problem, it’s better to write the entire template to a temporary buffer first then copy it to the ResponseWriter once we know that it has successfully executed. Here’s how it can be achieved using the bytes package:

package main

import (
	"bytes"
	"html/template"
	"net/http"
)

var tpl = template.Must(template.ParseFiles("index.html"))

func handler(w http.ResponseWriter, r *http.Request) {
	// Create an empty buffer that is ready to use
	var buf bytes.Buffer

	// Write the template to the buffer first
	err := tpl.Execute(&buf, nil)
	if err != nil {
		// Handle the error if any and return
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Template executed successfully to the buffer.
	// Now, copy it over to the ResponseWriter
	// This implies a 200 OK status code
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	buf.WriteTo(w)
}

This way, we can render the appropriate error page if something happens when executing the template. This practice also makes it easier to test our HTTP handlers because we now know for sure that a 200 OK response means that the template rendered successfully.

If you don’t want to create a new empty buffer for every request, you can use a buffer pool to help reduce allocations and garbage collection.

Thanks for reading, and happy coding!