{"record":{"id":"ad95c040380bed42","repo":"hashicorp/packer","slug":"failed-to-close-temp-file-w","errorCode":null,"errorMessage":"failed to close temp file: %w","messagePattern":"failed to close temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/hcp-sbom/packer_release_fetch.go","lineNumber":138,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"HTTP request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\t_ = f.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"HTTP %d for %s\", resp.StatusCode, url)\n\t}\n\n\t_, copyErr := io.Copy(f, resp.Body)\n\tcloseErr := f.Close()\n\tif copyErr != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"failed to write download: %w\", copyErr)\n\t}\n\tif closeErr != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"failed to close temp file: %w\", closeErr)\n\t}\n\n\treturn tmpPath, nil\n}\n\n// downloadChecksumFile fetches the SHA256SUMS text file at url.\nfunc downloadChecksumFile(ctx context.Context, client *http.Client, url string) (string, error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to build request for %s: %w\", url, err)\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to download %s: %w\", url, err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/hcp-sbom/packer_release_fetch.go#L120-L156","documentation":"downloadURLToTempFile buffers the downloaded Packer release zip into an os.CreateTemp file and closes it after io.Copy. This error wraps any *os.PathError returned by f.Close(), meaning the final flush/sync of buffered data to disk (or the file-descriptor release) failed. Because the close already failed, the temp file is deleted and the caller must retry; a partial or corrupt file is never returned.","triggerScenarios":"f.Close() returns a non-nil error after a successful io.Copy from the HTTP response body — typically ENOSPC (disk full), EIO (disk/device error), or an fsync-level failure while flushing written bytes in downloadURLToTempFile (packer_release_fetch.go:131-138).","commonSituations":"The disk or tmpfs backing os.TempDir() (e.g. /tmp) is full or nearly full after streaming a large zip; the container/pod ephemeral storage quota is exhausted; underlying storage encountered I/O errors; or a filesystem was unmounted/remounted mid-download.","solutions":["Check free space on the filesystem backing os.TempDir() (df -h /tmp) and free space or point TMPDIR to a volume with room for the Packer zip","Set the TMPDIR environment variable to a larger volume and rerun the hcp-sbom provisioning step","Check dmesg / container runtime logs for I/O errors or ephemeral-storage limits and fix the underlying disk or raise the quota","Retry the operation — the code already removed the bad temp file, and downloadPackerRelease wraps this in a retry.Config with 3 tries"],"exampleFix":"// before: trusting tmpfs default\n// (download fails with \"failed to close temp file: ... no space left on device\")\n// after: point temp files at a larger volume before running packer\n// $ export TMPDIR=/var/tmp   # volume with adequate free space\n// TMPDIR=/var/tmp packer build template.pkr.hcl","handlingStrategy":"validation","validationCode":"// before triggering the download flow, ensure the temp volume has room for the zip\nconst minFreeBytes = 200 << 20 // Packer zip is ~25-150MB; keep headroom\nvar st syscall.Statfs_t\nif err := syscall.Statfs(os.TempDir(), &st); err != nil {\n    return fmt.Errorf(\"cannot stat temp dir %s: %w\", os.TempDir(), err)\n}\nfree := uint64(st.Bavail) * uint64(st.Bsize)\nif free < minFreeBytes {\n    return fmt.Errorf(\"only %d bytes free in %s; set TMPDIR to a larger volume\", free, os.TempDir())\n}","typeGuard":"// ensure the wrapped cause is a filesystem (PathError) problem you can act on\nfunc asPathError(err error) (*fs.PathError, bool) {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        return pe, true\n    }\n    return nil, false\n}\n// usage: if pe, ok := asPathError(err); ok && errors.Is(pe.Err, syscall.ENOSPC) { ... }","tryCatchPattern":null,"preventionTips":["Monitor free space on the filesystem backing os.TempDir() before long builds","Point TMPDIR at a dedicated volume with headroom for ~150MB downloads","Raise container ephemeral-storage limits when running Packer in Kubernetes/CI containers","Watch dmesg for disk I/O errors on build hosts","Rely on the built-in 3-try retry in downloadPackerRelease for transient close failures"],"tags":["go","filesystem","disk-full","temp-file"],"backgroundTag":"temp-file-close-failed","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}