wavetermdev/waveterm · error
path is not a directory
Error message
path is not a directory
What it means
ReadDir lists directory contents, so after stat succeeds it asserts fileInfo.IsDir(). If the expanded path is a regular file, symlink-to-file, device, etc., this plain error is returned with no path embedded. The message is intentionally terse; the caller is expected to have passed a directory.
Source
Thrown at pkg/util/fileutil/readdir.go:50
Entries []DirEntryOut `json:"entries"`
EntryCount int `json:"entry_count"`
TotalEntries int `json:"total_entries"`
Truncated bool `json:"truncated,omitempty"`
}
func ReadDir(path string, maxEntries int) (*ReadDirResult, error) {
expandedPath, err := wavebase.ExpandHomeDir(path)
if err != nil {
return nil, fmt.Errorf("failed to expand path: %w", err)
}
fileInfo, err := os.Stat(expandedPath)
if err != nil {
return nil, fmt.Errorf("failed to stat path: %w", err)
}
if !fileInfo.IsDir() {
return nil, fmt.Errorf("path is not a directory")
}
entries, err := os.ReadDir(expandedPath)
if err != nil {
return nil, fmt.Errorf("failed to read directory: %w", err)
}
totalEntries := len(entries)
isDirMap := make(map[string]bool)
symlinkCount := 0
for _, entry := range entries {
name := entry.Name()
if entry.Type()&fs.ModeSymlink != 0 {
if symlinkCount < 1000 {
symlinkCount++
fullPath := filepath.Join(expandedPath, name)
if info, err := os.Stat(fullPath); err == nil {View on GitHub (pinned to a4447c1563)
Solutions
- Pass the containing directory instead: strip the filename from the path.
- Stat the path in the caller and branch: use ReadDir for directories and a file-read API for files.
- If the target is a symlink to a directory it works fine; only symlink-to-file triggers this — resolve the real target to confirm.
- Add UI/logic validation that the user-selected path is a directory before invoking ReadDir.
Example fix
// before
fileutil.ReadDir("/etc/passwd", 100)
// after
fileutil.ReadDir("/etc", 100) Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(path)
if err != nil { return err }
if !info.IsDir() {
return fmt.Errorf("%s is a file; pass its parent directory", path)
} Type guard
func isDir(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir()
} Try / catch
res, err := fileutil.ReadDir(path, max)
if err != nil && err.Error() == "path is not a directory" {
// switch to a file-read API or use filepath.Dir(path)
} Prevention
- Stat and branch before calling: directory -> ReadDir, file -> file-read API.
- In file pickers, restrict selection to directories for directory APIs.
- Strip filenames with filepath.Dir when only the parent is needed.
When it happens
Trigger: Calling ReadDir with a file path instead of a directory path — e.g. ReadDir("/etc/passwd", 100) or ReadDir("main.go", ...).
Common situations: A variable holds a file path where a directory was expected; shell-style completion handed back a file; a symlink resolves to a file; code that assumed a config value is always a folder.
Related errors
- not a regular file: %s
- failed to expand path: %w
- failed to stat path: %w
- getting absolute path for %s: %w
- path is a directory, not an image file
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/2f09bfbdcac7f01c.
Report an issue: GitHub.