kopia/kopia · error

error downloading file

Error message

error downloading file

What it means

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.

Solutions

  1. Retry the comparison; transient network failures often resolve.
  2. Check free disk space on the temp volume for large files.
  3. Inspect the wrapped cause to distinguish read (network) vs write (disk) failures.
  4. Verify storage backend stability / increase client timeouts.

Example fix

// before
return errors.Wrap(iocopy.JustCopy(dst, src), "error downloading file")
// after: nothing changes in the library; callers should retry transient causes
// err -> "error downloading file: ...read tcp ...: connection reset" => rerun the diff with stable connectivity
Defensive patterns

Strategy: retry

Validate before calling

if err := checkConnectivity(ctx, rep); err != nil {
    return fmt.Errorf("storage backend unstable, aborting diff: %w", err)
}

Try / catch

var lastErr error
for attempt := 0; attempt < 3; attempt++ {
    lastErr = runDiff(ctx)
    if lastErr == nil || !strings.Contains(lastErr.Error(), "error downloading file") {
        break
    }
    time.Sleep(backoff(attempt))
}
return lastErr

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/76446aaf3fb3525f. Report an issue: GitHub.

Appendix: source

Thrown at internal/diff/diff.go:393

func downloadFile(ctx context.Context, f fs.File, fname string) error {
	if err := os.MkdirAll(filepath.Dir(fname), dirMode); err != nil {
		return errors.Wrap(err, "error making directory")
	}

	src, err := f.Open(ctx)
	if err != nil {
		return errors.Wrap(err, "error opening object")
	}
	defer src.Close() //nolint:errcheck

	dst, err := os.Create(fname) //nolint:gosec
	if err != nil {
		return errors.Wrap(err, "error creating file to edit")
	}

	defer dst.Close() //nolint:errcheck

	return errors.Wrap(iocopy.JustCopy(dst, src), "error downloading file")
}

// Stats returns aggregated statistics computed during snapshot comparison
// must be invoked after a call to Compare which populates ComparerStats struct.
func (c *Comparer) Stats() Stats {
	return c.stats
}

func (c *Comparer) output(statsOnly bool, msg string, args ...any) {
	if !statsOnly {
		fmt.Fprintf(c.out, msg, args...) //nolint:errcheck
	}
}

// NewComparer creates a comparer for a given repository that will output the results to a given writer.
func NewComparer(out io.Writer, statsOnly bool) (*Comparer, error) {
	tmp, err := os.MkdirTemp("", "kopia")
	if err != nil {

View on GitHub (pinned to 82495e54b5)