Ignore Errors in Go on Purpose and Document Why You Did
When ignoring an error in Go is correct, how to do it visibly with the blank identifier and a comment, and which standard library calls never fail.

errcheck is a useful tool partly because ignoring errors is, sometimes, right. A rule of "never ignore" produces code that logs failed to close temporary file thirty times a day and is trusted by no one. The professional position is narrower: ignore an error only when nothing sensible can be done with it, and make the decision visible so the next reader and the linter agree it was intended.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
This note covers how to ignore properly and which cases qualify. It belongs to the error handling guide and works with finding unchecked errors. Everything ran on Go 1.25.6.
Make it visible
There are two ways to drop an error. One is to call the function as a statement, which compiles and leaves no trace. The other is to assign the result to the blank identifier, which says in the source that you saw it:
package main
import (
"fmt"
"os"
)
func main() {
path := os.TempDir() + "/ignore-demo.txt"
os.WriteFile(path, []byte("x"), 0o644)
// Best-effort cleanup: the file is a temp file and nobody can act on a failure.
_ = os.Remove(path)
_, err := os.Stat(path)
fmt.Println(os.IsNotExist(err))
}trueThe bare os.WriteFile(path, ...) ignores an error that matters, since the rest of the program depends on the file. That line is a bug of the kind errcheck finds. The _ = os.Remove(path) is deliberate and says so. Both compile, and only the second states an intention. Use the blank assignment, give a reason in a comment, and keep the reason to a clause.
Calls that cannot fail
Some functions return an error to satisfy an interface and never produce one. The documentation says so, and then ignoring it is correct:
package main
import (
"bytes"
"crypto/sha256"
"fmt"
"strings"
)
func main() {
var buf bytes.Buffer
n, err := buf.WriteString("hello")
fmt.Println(n, err)
var sb strings.Builder
n, err = sb.WriteString("world")
fmt.Println(n, err)
h := sha256.New()
n, err = h.Write([]byte("data"))
fmt.Println(n, err, h.Sum(nil)[0])
}5 <nil>
5 <nil>
4 <nil> 58bytes.Buffer.Write and strings.Builder.Write always return a nil error, and hash.Hash.Write is documented never to return one. The Buffer can panic with ErrTooLarge if it runs out of memory, but it will not return an error. Writing _, _ = h.Write(data) or leaving it unchecked is accepted practice, and linters commonly exclude these calls. Check the documentation before assuming: bufio.Writer.Write does return errors, and so does os.File.Write.
Cleanup on a failure path
When an operation has already failed and you are tidying up, a second error has nowhere good to go. Returning it would replace the more important first error, and logging it adds noise to a situation already being reported. The usual treatment is to ignore, visibly:
if err := process(f); err != nil {
_ = f.Close() // the first error is the one that matters
return err
}If both matter, join them with errors.Join instead (errors.Join). Closing a file you only read is the other common case (errors from deferred Close calls).
Never ignore these
- A write you need to keep.
CloseorSyncon a file you wrote, aFlushon a buffered writer, a databaseCommit. - Parsing input.
n, _ := strconv.Atoi(s)turns bad data into a silent zero. - Anything you branch on later. If the next line assumes it worked, check that it did.
- Security checks. An ignored error from an authorisation or signature check is a vulnerability.
A good habit is to review every _ = and _, _ = in a diff. Each should have a reason you could say out loud, and if the reason is "I wasn't sure what to do", then the error needs handling. The Go code review comments set the default firmly: do not discard errors using _ variables, and handle the error, return it, or in truly exceptional situations panic. Everything above is the narrow exception to that default, which is why each one should carry its reason in a comment.


