How to Check Whether a File Exists in Go
Checking whether a file exists is a common task when working with configuration, local state, generated output, or other filesystem resources. Go’s os package provides the necessary APIs, but it is important to distinguish “does not exist” from other errors such as permission failures.
1. Check with os.Stat
os.Stat returns file metadata when the path can be resolved:
package main
import (
"errors"
"fmt"
"os"
)
func fileExists(filename string) (bool, error) {
_, err := os.Stat(filename)
if err == nil {
return true, nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}
func main() {
filename := "example.txt"
exists, err := fileExists(filename)
if err != nil {
fmt.Println("Error:", err)
return
}
if exists {
fmt.Printf("File '%s' exists.\n", filename)
} else {
fmt.Printf("File '%s' does not exist.\n", filename)
}
}Returning an error prevents permission or I/O problems from being mistaken for a missing file.
2. Distinguish Files from Directories
If the path must specifically refer to a regular file:
func fileExists(filename string) (bool, error) {
info, err := os.Stat(filename)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}
if info.IsDir() {
return false, fmt.Errorf("'%s' is a directory, not a file", filename)
}
return true, nil
}Depending on your requirements, you may also want to check info.Mode().IsRegular() rather than merely rejecting directories.
3. Open the File Directly When You Need to Read It
If the next operation is to open the file anyway, avoid a separate existence check. Checking first creates a race: the file can disappear or change between Stat and Open.
file, err := os.Open("example.txt")
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fmt.Println("File does not exist")
return
}
fmt.Println("Open error:", err)
return
}
defer file.Close()
// Read from file here.This “attempt the operation and handle the error” pattern is often the safest approach.
Best Practices
- Use
os.Statwhen you truly need metadata or an existence check before presenting information to a user. - Open the file directly if you intend to read it immediately afterward.
- Use
errors.Is(err, os.ErrNotExist)oros.IsNotExist(err)for missing-path errors. - Do not treat every error as “file missing”; permission and filesystem errors need different handling.
- Remember that existence checks cannot guarantee the file will still exist by the time you use it.
Conclusion
Go makes filesystem checks straightforward, but robust code should preserve the distinction between a missing path and an operational error. Use os.Stat for metadata and direct os.Open calls when access is the real goal.