{"record":{"id":"76446aaf3fb3525f","repo":"kopia/kopia","slug":"error-downloading-file","errorCode":null,"errorMessage":"error downloading file","messagePattern":"error downloading file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/diff/diff.go","lineNumber":393,"sourceCode":"func downloadFile(ctx context.Context, f fs.File, fname string) error {\n\tif err := os.MkdirAll(filepath.Dir(fname), dirMode); err != nil {\n\t\treturn errors.Wrap(err, \"error making directory\")\n\t}\n\n\tsrc, err := f.Open(ctx)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"error opening object\")\n\t}\n\tdefer src.Close() //nolint:errcheck\n\n\tdst, err := os.Create(fname) //nolint:gosec\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"error creating file to edit\")\n\t}\n\n\tdefer dst.Close() //nolint:errcheck\n\n\treturn errors.Wrap(iocopy.JustCopy(dst, src), \"error downloading file\")\n}\n\n// Stats returns aggregated statistics computed during snapshot comparison\n// must be invoked after a call to Compare which populates ComparerStats struct.\nfunc (c *Comparer) Stats() Stats {\n\treturn c.stats\n}\n\nfunc (c *Comparer) output(statsOnly bool, msg string, args ...any) {\n\tif !statsOnly {\n\t\tfmt.Fprintf(c.out, msg, args...) //nolint:errcheck\n\t}\n}\n\n// NewComparer creates a comparer for a given repository that will output the results to a given writer.\nfunc NewComparer(out io.Writer, statsOnly bool) (*Comparer, error) {\n\ttmp, err := os.MkdirTemp(\"\", \"kopia\")\n\tif err != nil {","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/internal/diff/diff.go#L375-L411","documentation":"downloadFile streams the object's content into the local file with iocopy.JustCopy(dst, src). Any read/write error during the copy is wrapped as 'error downloading file'. This means the object download started but failed partway through.","triggerScenarios":"iocopy.JustCopy returns an error during downloadFile: the storage read stream breaks mid-transfer, the local write fails (disk full), or the context is cancelled during the copy.","commonSituations":"Flaky network to S3/Blob storage during large-file diffs; temp volume filling while copying a large snapshot file; connection resets/timeouts from the object store.","solutions":["Retry the comparison; transient network failures often resolve.","Check free disk space on the temp volume for large files.","Inspect the wrapped cause to distinguish read (network) vs write (disk) failures.","Verify storage backend stability / increase client timeouts."],"exampleFix":"// before\nreturn errors.Wrap(iocopy.JustCopy(dst, src), \"error downloading file\")\n// after: nothing changes in the library; callers should retry transient causes\n// err -> \"error downloading file: ...read tcp ...: connection reset\" => rerun the diff with stable connectivity","handlingStrategy":"retry","validationCode":"if err := checkConnectivity(ctx, rep); err != nil {\n    return fmt.Errorf(\"storage backend unstable, aborting diff: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"var lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n    lastErr = runDiff(ctx)\n    if lastErr == nil || !strings.Contains(lastErr.Error(), \"error downloading file\") {\n        break\n    }\n    time.Sleep(backoff(attempt))\n}\nreturn lastErr","preventionTips":["Run diffs on stable network links; avoid cellular/VPN flakiness for large files","Increase object-store client timeouts","Ensure temp volume can hold the largest file in either snapshot","Retry with exponential backoff on transient copy failures"],"tags":["io","copy","network","storage"],"backgroundTag":"network-request-failed","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}