{"record":{"id":"11ce35c897c822ac","repo":"hasura/graphql-engine","slug":"saving-downloaded-file-w","errorCode":null,"errorMessage":"saving downloaded file: %w","messagePattern":"saving downloaded file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/update/update.go","lineNumber":94,"sourceCode":"\tdefer res.Body.Close()\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn nil, errors.E(op, errors.E(\"could not find the release asset\"))\n\t}\n\n\tasset, err := os.OpenFile(\n\t\tfilepath.Join(filePath, fileName),\n\t\tos.O_CREATE|os.O_WRONLY|os.O_TRUNC,\n\t\t0o755,\n\t)\n\tif err != nil {\n\t\treturn nil, errors.E(op, fmt.Errorf(\"creating new binary file: %w\", err))\n\t}\n\tdefer asset.Close()\n\n\t_, err = io.Copy(asset, res.Body)\n\tif err != nil {\n\t\treturn nil, errors.E(op, fmt.Errorf(\"saving downloaded file: %w\", err))\n\t}\n\n\treturn asset, nil\n}\n\n// HasUpdate tells us if there is a new stable or prerelease update available.\nfunc HasUpdate(\n\tcurrentVersion *semver.Version,\n\ttimeFile string,\n) (bool, *semver.Version, bool, *semver.Version, error) {\n\tvar op errors.Op = \"update.HasUpdate\"\n\n\tif timeFile != \"\" {\n\t\tdefer func() {\n\t\t\terr := writeTimeToFile(timeFile, time.Now().UTC())\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"failed writing last update check time: \", err)\n\t\t\t}","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/cli/update/update.go#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check network stability and retry the update; the download is not resumable so a fresh attempt restarts it.","Verify free disk space and write permissions in the executable's directory (df -h, ls -ld $(dirname $(which cli))).","Manually delete any leftover .<exeName>.new partial file before retrying.","If behind a proxy, ensure HTTPS streaming to the release URL is not being cut off."],"exampleFix":"// before\nasset, err := downloadAsset(url, \".cli.new\", exePath)\nif err != nil {\n    return err // partial file left behind\n}\n\n// after (caller-side hygiene)\nasset, err := downloadAsset(url, \".cli.new\", exePath)\nif err != nil {\n    os.Remove(filepath.Join(exePath, \".cli.new\")) // clean partial file\n    return err\n}","handlingStrategy":"retry","validationCode":"if free, err := diskFree(exePath); err == nil && free < minAssetBytes {\n    return fmt.Errorf(\"insufficient disk space: %d bytes free\", free)\n}","typeGuard":null,"tryCatchPattern":"// Go: wrap and clean up partial file\nasset, err := downloadAsset(url, name, dir)\nif err != nil {\n    os.Remove(filepath.Join(dir, name))\n    return fmt.Errorf(\"download failed: %w\", err)\n}","preventionTips":["Keep sufficient free disk space in the install directory.","Pre-check write permissions before starting an update.","Retry once — mid-stream network drops are often transient."],"tags":["go","self-update","download","io","disk-full"],"backgroundTag":"file-write-failed","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}