This post was written by Markus Stefanko

Go Anti-Patterns : os.IsExist(err) != !os.IsNotExist(err)

This article was first published 3 years ago as a gist.

os.IsExist(err) != !os.IsNotExist(err)

An old Golang anti-pattern is to mis-use the functions os.IsExist(err) and os.IsNotExist(err), thinking that they are the opposite of each other. But that couldn’t be further from the truth.

They are error checkers, so use them only when err != nil, and you want to handle specific errors in a different way!

Their main purpose is to wrap around OS error messages for you, so you don’t have to spend time to check for Windows/Unix/Mobile/other OS error messages like "file exists/directory exists" and "file does not exist/directory does not exist"

This way you can handle such an error, while still failing on other unexpected errors like "permission denied""filesystem error", "wrong filename" error, etc.

Example 1

We want to create a symlink, but the target path exists already :

if _, err := os.Symlink("/old/path", "/path/to/whatever"); os.IsExist(err) {
// error happened, can't Symlink
// can't create symlink because /path/to/whatever already exists
}

Example 2

We want to stat a file, but it doesn’t exist :

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// error happened, can't Stat!
// /path/to/whatever does not exist
}

Anti-Pattern

And here comes the big BUT :

// Anti-pattern : We want to stat a file, and continue if it exists :
if _, err := os.Stat("/file/that/exists"); os.IsExist(err) {
// will never trigger!
// why? because os.Stat runs normally if file exists.
// it's expected behaviour for os.Stat, so it doesn't throw an error
// os.IsExist() does not receive an error ( it's nil ), so it can't tell you
// if the error message was "file not found"
}

Instead we want to stat a file, and continue if it exists :

_, err := os.Stat("/file/that/exists");
if err != nil {
if os.IsNotExist(err) {
// file does not exist, do something
} else {
// more serious errors
}
}
// file exists.. continue with code here, or in else statement or specify if err == nil { // do something }

os.IsExist(err) is good for cases when you expect the file to not exist yet, but the file actually exists :

os.Symlink("/path/that/exists", "/path/to/symlink/target")
// os.IsExist(err) will trigger when target exists already
os.Mkdir(target)
// os.IsExist(err) will trigger because target path already exists
os.OpenFile(target, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
// os.IsExist(err) will trigger when target exists because O_EXCL means that file should not exist yet

os.IsNotExist(err) is good for more common cases where you expect the file to exists, but it actually doesn’t exist :

os.Chdir()
os.Stat()
os.Open()
os.OpenFile() // without os.O_EXCL
os.Chmod()
os.Chown()
os.Close()
os.Read()
os.ReadAt()
os.ReadDir()
os.Readdirnames()
os.Seek()
os.Truncate()
os.Write()
os.WriteAt()
os.WriteString()

Further reading on this topic :

by Markus Stefanko