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

  1. 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).
  2. Verify backupBaseDir is actually a directory: ls -la <backupBaseDir>; recreate it with mkdir if it is a file or missing.
  3. If a race with directory removal is expected, treat fs.ErrNotExist from ReadDir as benign (nothing to clean) in the caller.
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/87d9ff35f138643d. Report an issue: GitHub.