{"record":{"id":"66151f8698d17675","repo":"henrygd/beszel","slug":"write-tmp-w","errorCode":null,"errorMessage":"write tmp: %w","messagePattern":"write tmp: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tools/fetchsmartctl/main.go","lineNumber":97,"sourceCode":"\t\tcase 40:\n\t\t\thasher = sha1.New()\n\t\tcase 64:\n\t\t\thasher = sha256.New()\n\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)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/tools/fetchsmartctl/main.go#L79-L115","documentation":"Thrown by downloadFile when io.Copy fails writing the response body into the temp file (via the optional MultiWriter that also feeds the hasher). It wraps either a disk write error or a network read error from the response body mid-transfer. The temp file is closed and removed on failure.","triggerScenarios":"Disk fills during the download (ENOSPC), the connection drops mid-body (unexpected EOF, connection reset), or the filesystem rejects the write partway.","commonSituations":"Flaky Wi-Fi/CI network interrupting large downloads; full disk or small tmpfs holding the destination; proxy killing long-lived connections; quota exceeded.","solutions":["Inspect the wrapped %w error: 'no space left' → free disk space; 'connection reset/unexpected EOF' → network issue","Retry the download — transient network drops are the most common cause","Ensure destination volume has free space larger than the binary size","Increase client timeout or add resume/retry logic for large files"],"exampleFix":"// before\nif _, err := io.Copy(mw, resp.Body); err != nil {\n\tf.Close()\n\tos.Remove(tmp)\n\treturn fmt.Errorf(\"write tmp: %w\", err)\n}\n// after\nif _, err := io.Copy(mw, resp.Body); err != nil {\n\tf.Close()\n\tos.Remove(tmp)\n\treturn fmt.Errorf(\"write tmp (disk full or connection dropped?): %w\", err)\n}","handlingStrategy":"retry","validationCode":"if free, err := diskFree(filepath.Dir(dest)); err == nil && free < minRequiredBytes {\n\treturn fmt.Errorf(\"only %d bytes free, need ~%d\", free, minRequiredBytes)\n}","typeGuard":null,"tryCatchPattern":"var lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n\tlastErr = downloadFile(url, dest, sha)\n\tif lastErr == nil || !strings.HasPrefix(lastErr.Error(), \"write tmp:\") {\n\t\tbreak\n\t}\n\ttime.Sleep(time.Duration(1<<attempt) * time.Second)\n}\nif lastErr != nil {\n\treturn lastErr\n}","preventionTips":["Ensure the destination volume has free space larger than the expected download","Retry downloads once on transient network errors","Avoid downloading over flaky links or unstable proxies","Use wired/stable network or resumable HTTP (Range) for large files"],"tags":["network","filesystem","download"],"backgroundTag":"download-interrupted","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}