Searches like golang os.stat, os.stat golang, golang os stat, go os stat, or os stat golang all point at the same API: os.Stat returns metadata about a path without opening it for I/O. The result is an fs.FileInfo (historically documented under os; the interface lives in io/fs today). For open handles, (*os.File).Stat answers the same questions for the file descriptor you already hold. This page is about that metadata path; for general reads and writes, see the broader golang os package article and reading and updating the same file.
Tested with Go 1.24 on Linux.
golang os.Stat and fs.FileInfo
func Stat(name string) (fs.FileInfo, error) stats the named file. On success you get methods such as Name, Size, Mode, ModTime, and IsDir. On failure the error is typically a *fs.PathError (older docs call it *os.PathError; it implements error and unwraps to the underlying cause).
Prefer printing a few fields instead of %+v on the concrete FileInfo implementation, which dumps unexported layout that changes between releases:
package main
import (
"fmt"
"log"
"os"
"time"
)
func main() {
fi, err := os.Stat("statdemo.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("name=%s size=%d mode=%s mod=%s isDir=%v\n",
fi.Name(), fi.Size(), fi.Mode(), fi.ModTime().UTC().Format(time.RFC3339), fi.IsDir())
}name=statdemo.txt size=16 mode=-rw-rw-r-- mod=2026-06-17T18:42:45Z isDir=falseCreate statdemo.txt in the current directory before running; your size, mode, and timestamp will differ.
(*os.File).Stat after os.Open
If you already opened the path, Stat on the file uses the same descriptor (useful right after Open or Create):
package main
import (
"fmt"
"log"
"os"
"time"
)
func main() {
f, err := os.Open("statdemo.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
log.Fatal(err)
}
fmt.Printf("from File.Stat: name=%s size=%d mod=%s\n",
fi.Name(), fi.Size(), fi.ModTime().UTC().Format(time.RFC3339))
}from File.Stat: name=statdemo.txt size=16 mod=2026-06-17T18:42:45ZExistence checks with os.stat / os.Stat
The usual os.stat go pattern for “does this path exist?” is to call os.Stat and inspect the error. Use errors.Is with os.ErrNotExist so wrapped errors still work:
package main
import (
"errors"
"fmt"
"os"
)
func pathExists(name string) (bool, error) {
_, err := os.Stat(name)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}
return true, nil
}
func main() {
for _, p := range []string{"statdemo.txt", "nope.txt"} {
ok, err := pathExists(p)
fmt.Println(p, ok, err)
}
}statdemo.txt true <nil>
nope.txt false <nil>If you need “exists and is a regular file,” combine Stat with !info.IsDir() (and optionally info.Mode().IsRegular() on platforms where that matters). A directory path still exists: Stat succeeds and IsDir() is true.
os.IsNotExist is still available, but errors.Is(err, os.ErrNotExist) is the idiomatic choice in new code.
os.Lstat and symbolic links
os.Lstat is like Stat but does not follow a final symbolic link: you see the link’s own metadata. os.Stat follows links and describes the target. Pick Lstat when symlink identity matters (for example tools that delete or rewrite the link itself).
Summary
Golang os.stat style lookups use os.Stat to obtain an fs.FileInfo without opening the file for reads or writes, or (*os.File).Stat when you already have a handle. For “not found,” rely on errors.Is(err, os.ErrNotExist) rather than string matching on errors. Use os.Lstat when you must not follow symlinks. For printing structs for debugging, see printing structs in Go, but prefer explicit fields for FileInfo in documentation and logs.

