hasura/graphql-engine · error

saving downloaded file: %w

Error message

saving downloaded file: %w

What it means

Raised by downloadAsset in the CLI self-updater when io.Copy fails while streaming a downloaded release asset to a newly created binary file (cli/update/update.go:94). It wraps the underlying I/O error, so the root cause may be a dropped network connection or a local disk problem. The partially written file is not cleaned up automatically by this error path.

Source

Thrown at cli/update/update.go:94

	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return nil, errors.E(op, errors.E("could not find the release asset"))
	}

	asset, err := os.OpenFile(
		filepath.Join(filePath, fileName),
		os.O_CREATE|os.O_WRONLY|os.O_TRUNC,
		0o755,
	)
	if err != nil {
		return nil, errors.E(op, fmt.Errorf("creating new binary file: %w", err))
	}
	defer asset.Close()

	_, err = io.Copy(asset, res.Body)
	if err != nil {
		return nil, errors.E(op, fmt.Errorf("saving downloaded file: %w", err))
	}

	return asset, nil
}

// HasUpdate tells us if there is a new stable or prerelease update available.
func HasUpdate(
	currentVersion *semver.Version,
	timeFile string,
) (bool, *semver.Version, bool, *semver.Version, error) {
	var op errors.Op = "update.HasUpdate"

	if timeFile != "" {
		defer func() {
			err := writeTimeToFile(timeFile, time.Now().UTC())
			if err != nil {
				fmt.Fprintln(os.Stderr, "failed writing last update check time: ", err)
			}

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Check network stability and retry the update; the download is not resumable so a fresh attempt restarts it.
  2. Verify free disk space and write permissions in the executable's directory (df -h, ls -ld $(dirname $(which cli))).
  3. Manually delete any leftover .<exeName>.new partial file before retrying.
  4. If behind a proxy, ensure HTTPS streaming to the release URL is not being cut off.

Example fix

// before
asset, err := downloadAsset(url, ".cli.new", exePath)
if err != nil {
    return err // partial file left behind
}

// after (caller-side hygiene)
asset, err := downloadAsset(url, ".cli.new", exePath)
if err != nil {
    os.Remove(filepath.Join(exePath, ".cli.new")) // clean partial file
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

if free, err := diskFree(exePath); err == nil && free < minAssetBytes {
    return fmt.Errorf("insufficient disk space: %d bytes free", free)
}

Try / catch

// Go: wrap and clean up partial file
asset, err := downloadAsset(url, name, dir)
if err != nil {
    os.Remove(filepath.Join(dir, name))
    return fmt.Errorf("download failed: %w", err)
}

Prevention

When it happens

Trigger: Calling ApplyUpdate(v) (or the update command that calls it) where the HTTP response body is interrupted mid-download, the disk is full, or the target directory ('.<exeName>.new' next to the executable) becomes unwritable mid-copy.

Common situations: Flaky network dropping the GitHub release download halfway; disk full in the install directory (e.g. /usr/local/bin on a full partition); antivirus/lockers on Windows touching the .new file while it is written.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/11ce35c897c822ac. Report an issue: GitHub.