golang/go · error · fs.PathError
not a directory
Error message
not a directory
What it means
Returned by cache.Open(dir) when os.Stat(dir) succeeds but info.IsDir() is false — i.e. GOCACHE points at an existing file, not a directory. The disk cache needs a directory because it creates 256 sub-buckets (00..ff) under it. Wrapping as fs.PathError{Op:"open"} mirrors the stdlib open semantics.
Source
Thrown at src/cmd/go/internal/cache/cache.go:101
// Open opens and returns the cache in the given directory.
//
// It is safe for multiple processes on a single machine to use the
// same cache directory in a local file system simultaneously.
// They will coordinate using operating system file locks and may
// duplicate effort but will not corrupt the cache.
//
// However, it is NOT safe for multiple processes on different machines
// to share a cache directory (for example, if the directory were stored
// in a network file system). File locking is notoriously unreliable in
// network file systems and may not suffice to protect the cache.
func Open(dir string) (*DiskCache, error) {
info, err := os.Stat(dir)
if err != nil {
return nil, err
}
if !info.IsDir() {
return nil, &fs.PathError{Op: "open", Path: dir, Err: fmt.Errorf("not a directory")}
}
for i := 0; i < 256; i++ {
name := filepath.Join(dir, fmt.Sprintf("%02x", i))
if err := os.MkdirAll(name, 0o777); err != nil {
return nil, err
}
}
c := &DiskCache{
dir: dir,
now: time.Now,
}
return c, nil
}
// fileName returns the name of the file corresponding to the given id.
func (c *DiskCache) fileName(id [HashSize]byte, key string) string {
return filepath.Join(c.dir, fmt.Sprintf("%02x", id[0]), fmt.Sprintf("%x", id)+"-"+key)
}View on GitHub (pinned to b6b368adc5)
Solutions
- Remove the offending file at the GOCACHE path so the cache can create the directory itself.
- Point GOCACHE at an existing (or creatable) directory path.
- Run `go clean -cache` after fixing the path to start fresh.
- Audit provisioning scripts that touch the cache path.
Example fix
// before: GOCACHE points at a regular file $ GOCACHE=$HOME/.cache/go-build go build . // -> open ... not a directory // after: remove the file, let go recreate the directory $ rm -f "$HOME/.cache/go-build" $ GOCACHE=$HOME/.cache/go-build go build .
Defensive patterns
Strategy: validation
Validate before calling
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
return fmt.Errorf("refusing to use %s: not a directory", dir)
}
c, err := cache.Open(dir) Try / catch
c, err := cache.Open(dir)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && perr.Err.Error() == "not a directory" {
// remove the offending file and retry once
_ = os.Remove(dir)
c, err = cache.Open(dir)
}
} Prevention
- Validate GOCACHE points at a directory in provisioning scripts.
- Never touch a marker file at the cache path.
- Run `go env GOCACHE` to confirm the resolved path.
When it happens
Trigger: GOCACHE (or a caller-supplied cache dir) resolves to a regular file or a symlink to a file; cache.Open stats it, sees it is not a directory, and aborts before MkdirAll runs.
Common situations: A leftover file (e.g. `touch ~/.cache/go-build` by accident); GOCACHE typo colliding with an existing file; provisioning scripts that created a marker file at the cache path.
Related errors
- GOCACHE is not defined and %v
- GOCACHE is not an absolute path
- file content changed underfoot
- %s %s: missing ziphash: %v
- failed to create cache directory: %w
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/82411a337a1b2019.
Report an issue: GitHub.