wavetermdev/waveterm · error
failed to read backup directory: %w
Error message
failed to read backup directory: %w
What it means
CleanupOldBackups walks backupBaseDir to delete backups older than BackupRetentionPeriod. Before iterating it calls os.ReadDir(backupBaseDir); if the OS-level read fails (permissions, the path being a file, or a race where the directory disappears between the os.Stat existence check and the ReadDir), the underlying error is wrapped with this message and returned to the cleanup loop.
Source
Thrown at pkg/filebackup/filebackup.go:135
err = os.WriteFile(restoreToFileName, backupData, perm)
if err != nil {
return fmt.Errorf("failed to restore file: %w", err)
}
return nil
}
func CleanupOldBackups() error {
backupBaseDir := filepath.Join(wavebase.GetWaveCachesDir(), "waveai-backups")
if _, err := os.Stat(backupBaseDir); os.IsNotExist(err) {
return nil
}
entries, err := os.ReadDir(backupBaseDir)
if err != nil {
return fmt.Errorf("failed to read backup directory: %w", err)
}
cutoffTime := time.Now().Add(-BackupRetentionPeriod)
var removedCount int
for _, entry := range entries {
if !entry.IsDir() {
continue
}
dirPath := filepath.Join(backupBaseDir, entry.Name())
info, err := entry.Info()
if err != nil {
log.Printf("failed to get info for backup dir %s: %v\n", entry.Name(), err)
continue
}
if info.ModTime().Before(cutoffTime) {View on GitHub (pinned to a4447c1563)
Solutions
- Check the wrapped cause (%w) to identify the OS error; for permission problems fix ownership/permissions on the backup directory (e.g. chown/chmod so the running user can read it).
- Verify backupBaseDir is actually a directory: ls -la <backupBaseDir>; recreate it with mkdir if it is a file or missing.
- If a race with directory removal is expected, treat fs.ErrNotExist from ReadDir as benign (nothing to clean) in the caller.
- Check mount/disk health if the cause is an I/O error (dmesg, remount read-write).
Example fix
// before
entries, err := os.ReadDir(backupBaseDir)
if err != nil {
return fmt.Errorf("failed to read backup directory: %w", err)
}
// after
entries, err := os.ReadDir(backupBaseDir)
if err != nil {
if os.IsNotExist(err) {
return nil // directory vanished between Stat and ReadDir; nothing to clean
}
return fmt.Errorf("failed to read backup directory: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go has no pre-call check API; verify the path is a readable directory first
if info, err := os.Stat(backupBaseDir); err != nil || !info.IsDir() {
// recreate or fix backupBaseDir before the cleanup loop runs
os.MkdirAll(backupBaseDir, 0o755)
} Try / catch
if err := CleanupOldBackups(ctx); err != nil {
var pathErr *os.PathError
if errors.As(err, &pathErr) {
log.Printf("backup dir unreadable (%s): %v; recreating", pathErr.Path, pathErr.Err)
os.MkdirAll(backupBaseDir, 0o755)
}
} Prevention
- Ensure the process user owns or can read the backup directory at startup.
- Provision backupBaseDir as a directory during install; never point it at a file.
- Treat fs.ErrNotExist from cleanup as benign in periodic loops so races don't alarm.
- Monitor mount health/read-only-remounts of the volume holding backups.
When it happens
Trigger: os.ReadDir(backupBaseDir) fails after the preceding os.Stat found the path exists: permissions changed, backupBaseDir was replaced by a regular file or symlink to a file, the directory was removed by another process between Stat and ReadDir, or an I/O error on the filesystem.
Common situations: Backups directory owned by another user or read-only after a restore; a misconfiguration points backupBaseDir at a file instead of a directory; cleanup loop racing with a wipe/reinstall of the backup directory; read-only mounts (e.g. container volume, disk failure).
Related errors
- reading file %s: %w
- error writing to file %s: %w
- failed to stat file for backup: %w
- failed to read file for backup: %w
- failed to create backup directory: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/87d9ff35f138643d.
Report an issue: GitHub.