wavetermdev/waveterm · error
failed to read directory: %w
Error message
failed to read directory: %w
What it means
ReadDir wraps errors from os.ReadDir. Unlike stat, this can fail even when the path is a valid directory if the directory cannot be opened — typically a permissions problem (no read bit) or the directory was removed between stat and read. The OS cause is preserved via %w.
Source
Thrown at pkg/util/fileutil/readdir.go:55
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 {
isDirMap[name] = info.IsDir()
} else {
isDirMap[name] = entry.IsDir()
}
} else {View on GitHub (pinned to a4447c1563)
Solutions
- Unwrap the cause: 'permission denied' -> chmod/chown the directory or run as a user with read access.
- Re-check the directory still exists; if it races away (e.g. under /proc or a tmp build dir), add existence handling and retry.
- Use `sudo -u <user> ls <dir>` to reproduce the access the library sees.
- If listing is optional, catch this error and degrade gracefully instead of failing the whole operation.
Example fix
// before: no error differentiation
res, err := fileutil.ReadDir(dir, 100)
// after
res, err := fileutil.ReadDir(dir, 100)
if err != nil {
if os.IsPermission(errors.Unwrap(err)) {
log.Warnf("skipping unreadable dir %s", dir)
return nil
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
if f, err := os.Open(path); err != nil {
return fmt.Errorf("directory not readable: %w", err)
} else { f.Close() } Try / catch
res, err := fileutil.ReadDir(path, max)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, os.ErrPermission) {
return nil // skip unreadable dir gracefully
}
return err
} Prevention
- Grant the process user read permission on directories it must enumerate.
- Treat volatile directories (/proc, tmp build dirs) as racy: retry or skip on failure.
- Test your listing flow under the actual service user, not just your dev shell.
When it happens
Trigger: os.ReadDir fails on an existing, statable directory: missing read (r) permission, directory deleted in a race, or I/O error on the underlying storage.
Common situations: Listing /root or another user's private directory as a normal user; container/agent sandbox without read access; directories like /proc/<pid> that vanish; NFS stale handles.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- reading file %s: %w
- failed to create backup directory: %w
- failed to create socket directory: %w
- failed to create log directory: %w
- failed to open log file: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/a04598c7fe2b8d78.
Report an issue: GitHub.