{"record":{"id":"82468bead2d5bbb5","repo":"henrygd/beszel","slug":"close-tmp-w","errorCode":null,"errorMessage":"close tmp: %w","messagePattern":"close tmp: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tools/fetchsmartctl/main.go","lineNumber":101,"sourceCode":"\t\tdefault:\n\t\t\tf.Close()\n\t\t\tos.Remove(tmp)\n\t\t\treturn fmt.Errorf(\"unsupported hash length: %d (expected 40 for SHA1 or 64 for SHA256)\", len(cleanSha))\n\t\t}\n\t}\n\n\tvar mw io.Writer = f\n\tif hasher != nil {\n\t\tmw = io.MultiWriter(f, hasher)\n\t}\n\tif _, err := io.Copy(mw, resp.Body); err != nil {\n\t\tf.Close()\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"write tmp: %w\", err)\n\t}\n\tif err := f.Close(); err != nil {\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"close tmp: %w\", err)\n\t}\n\n\tif hasher != nil && shaHex != \"\" {\n\t\tcleanSha := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(shaHex), \" \", \"\"))\n\t\tgot := strings.ToLower(hex.EncodeToString(hasher.Sum(nil)))\n\t\tif got != cleanSha {\n\t\t\tos.Remove(tmp)\n\t\t\treturn fmt.Errorf(\"hash mismatch: got %s want %s\", got, cleanSha)\n\t\t}\n\t}\n\n\t// Make executable and move into place\n\tif err := os.Chmod(tmp, 0o755); err != nil {\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"chmod: %w\", err)\n\t}\n\tif err := os.Rename(tmp, dest); err != nil {\n\t\tos.Remove(tmp)","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/tools/fetchsmartctl/main.go#L83-L119","documentation":"Thrown by downloadFile when f.Close() fails after writing the temp file. Close flushes buffered data, so this typically indicates a delayed write error such as ENOSPC surfaced at close time, though it can also be an I/O error from the filesystem. The temp file is removed so no partial download remains.","triggerScenarios":"f.Close() returns non-nil after io.Copy succeeded — rare, usually deferred disk-full or I/O errors becoming visible only at flush/close.","commonSituations":"Disk filling exactly during the write; network filesystem reporting I/O errors on close; quota enforcement flushing at close.","solutions":["Check the wrapped %w OS error — usually ENOSPC or EIO","Free disk space / raise quota and retry","Verify the filesystem health (dmesg / smartctl on the build machine)","Retry the download on a local disk instead of a network mount"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if err := syscall.Statfs(dir, &st); err == nil && uint64(st.Bavail)*uint64(st.Bsize) < minRequiredBytes {\n\treturn fmt.Errorf(\"insufficient free space in %s\", dir)\n}","typeGuard":null,"tryCatchPattern":"if err := downloadFile(url, dest, sha); err != nil {\n\tif strings.HasPrefix(err.Error(), \"close tmp:\") {\n\t\tfmt.Printf(\"disk/I-O problem on %s: %v — free space and retry\\n\", filepath.Dir(dest), err)\n\t}\n\treturn err\n}","preventionTips":["Keep ample headroom on the build volume (close() surfaces delayed ENOSPC)","Avoid network filesystems as download destinations","Monitor disk health (SMART) on build machines","Clean partial .tmp artifacts after failed downloads"],"tags":["filesystem","download","build-tool"],"backgroundTag":"disk-full","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}