wavetermdev/waveterm · error

failed to read file for backup: %w

Error message

failed to read file for backup: %w

What it means

After successfully statting the file, MakeFileBackup reads its full contents with os.ReadFile to store the snapshot. If the read fails — file deleted in the race window between Stat and Read, permission denied, or an I/O error — this wrapped error is returned and the edit/delete is aborted.

Source

Thrown at pkg/filebackup/filebackup.go:36

)

const BackupRetentionPeriod = 5 * 24 * time.Hour

type BackupMetadata struct {
	FullPath  string `json:"fullpath"`
	Timestamp string `json:"timestamp"`
	Perm      string `json:"perm"`
}

func MakeFileBackup(absFilePath string) (string, error) {
	fileInfo, err := os.Stat(absFilePath)
	if err != nil {
		return "", fmt.Errorf("failed to stat file for backup: %w", err)
	}

	fileData, err := os.ReadFile(absFilePath)
	if err != nil {
		return "", fmt.Errorf("failed to read file for backup: %w", err)
	}

	dir := filepath.Dir(absFilePath)
	basename := filepath.Base(absFilePath)

	hash := sha256.Sum256([]byte(dir))
	dirHash8 := hex.EncodeToString(hash[:])[:8]

	uuidV7, err := uuid.NewV7()
	if err != nil {
		return "", fmt.Errorf("failed to generate UUID: %w", err)
	}
	uuidStr := uuidV7.String()

	now := time.Now()
	dateStr := now.Format("2006-01-02")

	backupDir := filepath.Join(wavebase.GetWaveCachesDir(), "waveai-backups", dateStr)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Retry the operation — a transient race usually succeeds on a second attempt
  2. Verify read permission on the file (and search permission on parent dirs)
  3. Check filesystem health/disk errors in system logs if I/O errors repeat
  4. Re-open/refresh the file in Wave so it operates on the current state

Example fix

// before
fileData, err := os.ReadFile(absFilePath)
if err != nil { return "", fmt.Errorf("failed to read file for backup: %w", err) }
// after
fileData, err := os.ReadFile(absFilePath)
if os.IsNotExist(err) {
    return "", fmt.Errorf("file vanished between stat and read: %w", err) // caller can treat as deleted
}
Defensive patterns

Strategy: retry

Validate before calling

info, err := os.Stat(absFilePath)
if err == nil && info.Mode().Perm()&0o400 == 0 {
    return fmt.Errorf("file is not readable: %s", absFilePath)
}

Try / catch

backupPath, err := filebackup.MakeFileBackup(path)
if err != nil {
    if errors.Is(errors.Unwrap(err), os.IsPermission(nil)) || os.IsPermission(errors.Unwrap(err)) {
        return fmt.Errorf("cannot back up unreadable file: %w", err)
    }
    // transient race: retry once
    backupPath, err = filebackup.MakeFileBackup(path)
}

Prevention

When it happens

Trigger: os.ReadFile(absFilePath) fails after os.Stat succeeded: TOCTOU deletion, read permission missing, or disk I/O error.

Common situations: Another process deletes/truncates the file between Stat and Read; read-protected files (chmod 000) or root-owned files edited by non-root Wave; NFS/network filesystem hiccups.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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