{"record":{"id":"f8ff16dddbfc7d1c","repo":"wavetermdev/waveterm","slug":"failed-to-read-file-for-backup-w","errorCode":null,"errorMessage":"failed to read file for backup: %w","messagePattern":"failed to read file for backup: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filebackup/filebackup.go","lineNumber":36,"sourceCode":")\n\nconst BackupRetentionPeriod = 5 * 24 * time.Hour\n\ntype BackupMetadata struct {\n\tFullPath  string `json:\"fullpath\"`\n\tTimestamp string `json:\"timestamp\"`\n\tPerm      string `json:\"perm\"`\n}\n\nfunc MakeFileBackup(absFilePath string) (string, error) {\n\tfileInfo, err := os.Stat(absFilePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to stat file for backup: %w\", err)\n\t}\n\n\tfileData, err := os.ReadFile(absFilePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read file for backup: %w\", err)\n\t}\n\n\tdir := filepath.Dir(absFilePath)\n\tbasename := filepath.Base(absFilePath)\n\n\thash := sha256.Sum256([]byte(dir))\n\tdirHash8 := hex.EncodeToString(hash[:])[:8]\n\n\tuuidV7, err := uuid.NewV7()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to generate UUID: %w\", err)\n\t}\n\tuuidStr := uuidV7.String()\n\n\tnow := time.Now()\n\tdateStr := now.Format(\"2006-01-02\")\n\n\tbackupDir := filepath.Join(wavebase.GetWaveCachesDir(), \"waveai-backups\", dateStr)","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filebackup/filebackup.go#L18-L54","documentation":"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.","triggerScenarios":"os.ReadFile(absFilePath) fails after os.Stat succeeded: TOCTOU deletion, read permission missing, or disk I/O error.","commonSituations":"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.","solutions":["Retry the operation — a transient race usually succeeds on a second attempt","Verify read permission on the file (and search permission on parent dirs)","Check filesystem health/disk errors in system logs if I/O errors repeat","Re-open/refresh the file in Wave so it operates on the current state"],"exampleFix":"// before\nfileData, err := os.ReadFile(absFilePath)\nif err != nil { return \"\", fmt.Errorf(\"failed to read file for backup: %w\", err) }\n// after\nfileData, err := os.ReadFile(absFilePath)\nif os.IsNotExist(err) {\n    return \"\", fmt.Errorf(\"file vanished between stat and read: %w\", err) // caller can treat as deleted\n}","handlingStrategy":"retry","validationCode":"info, err := os.Stat(absFilePath)\nif err == nil && info.Mode().Perm()&0o400 == 0 {\n    return fmt.Errorf(\"file is not readable: %s\", absFilePath)\n}","typeGuard":null,"tryCatchPattern":"backupPath, err := filebackup.MakeFileBackup(path)\nif err != nil {\n    if errors.Is(errors.Unwrap(err), os.IsPermission(nil)) || os.IsPermission(errors.Unwrap(err)) {\n        return fmt.Errorf(\"cannot back up unreadable file: %w\", err)\n    }\n    // transient race: retry once\n    backupPath, err = filebackup.MakeFileBackup(path)\n}","preventionTips":["Retry once on failure — Stat/Read TOCTOU races are usually transient","Verify read permission (and that the file is a regular file, not /dev/null or a fifo)","Avoid editing files on flaky network mounts when possible","Re-open the file in Wave after external modifications to refresh state"],"tags":["filesystem","backup","go","io-error"],"backgroundTag":"file-read-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}