{"record":{"id":"ee1a61dd52076351","repo":"coreybutler/nvm-windows","slug":"panic-err-ee1a61","errorCode":null,"errorMessage":"panic(err)","messagePattern":"panic\\(err\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/web/web.go","lineNumber":451,"sourceCode":"\t\t}\n\t}\n\n\t// Check online to see if a 64 bit version exists\n\t_, err := client.Head(url)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn url\n}\n\nfunc unzip(src, dest string) error {\n\tr, err := zip.OpenReader(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err := r.Close(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\tos.MkdirAll(dest, 0755)\n\n\t// Closure to address file descriptors issue with all the deferred .Close() methods\n\textractAndWriteFile := func(f *zip.File) error {\n\t\trc, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\tif err := rc.Close(); err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}()\n\n\t\tpath := filepath.Join(dest, f.Name)","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/coreybutler/nvm-windows/blob/5b18223ca19ff50d707f35410dbc6bd440a9f74d/src/web/web.go#L433-L469","documentation":"This panic fires in the deferred close of the zip.OpenReader handle inside unzip. If closing the underlying file reader returns an error, the deferred function panics, aborting the process during cleanup. Note that zip.ReadCloser.Close only closes the embedded *os.File, so a non-nil close error signals an OS-level failure on the archive file handle, not a corrupt zip (that surfaces earlier at OpenReader).","triggerScenarios":"Calling unzip(src, dest) where the opened archive's file descriptor fails to close: file deleted or renamed on POSIX between open and close; mandatory locks or antivirus on Windows; NFS/SMB handle invalidation; or fd pressure causing EBADF. The panic happens after extraction work, in the deferred r.Close().","commonSituations":"Extracting a zip from a network share that drops the connection mid-operation; extracting an archive that another process moves/deletes (download-then-extract pipelines); Windows Defender briefly locking downloaded zip files; running with a low ulimit -n so handle state is corrupted.","solutions":["Change the deferred close to log-and-continue instead of panicking: defer func() { if err := r.Close(); err != nil { log.Printf(\"zip close: %v\", err) } }().","Verify the source path is stable and readable for the whole call: copy the archive to a local temp file before unzipping when it lives on a network mount.","Exclude the archive directory from antivirus scanning or add the app to Defender exclusions if locks recur on Windows.","Check available file descriptors (ulimit -n) and raise the limit for long extraction loops."],"exampleFix":"// before\ndefer func() {\n\tif err := r.Close(); err != nil {\n\t\tpanic(err)\n\t}\n}()\n\n// after\ndefer func() {\n\tif err := r.Close(); err != nil {\n\t\t// close failure after a successful read is non-fatal; surface it, don't crash\n\t\tlog.Printf(\"unzip: closing archive %s: %v\", src, err)\n\t}\n}()","handlingStrategy":"try-catch","validationCode":"// Cheap pre-checks before unzipping: archive exists, is readable, and opens as a zip\nfunc canUnzip(src string) error {\n\tfi, err := os.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif fi.Size() == 0 {\n\t\treturn fmt.Errorf(\"archive %s is empty (truncated download?)\", src)\n\t}\n\tr, err := zip.OpenReader(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn r.Close() // also exercises the close path once, up front\n}","typeGuard":null,"tryCatchPattern":"// Go: recover panics at the API boundary so a close failure degrades, not crashes\nfunc safeUnzip(src, dest string) (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"unzip %s: panic: %v\", src, r)\n\t\t}\n\t}()\n\treturn unzip(src, dest)\n}","preventionTips":["Copy archives from network mounts to a local temp file before extraction so the fd stays stable.","Pre-open and close the archive once (zip.OpenReader) to validate it before the real extraction.","Raise ulimit -n / handle limits in extraction-heavy services.","Prefer logging deferred Close errors over panicking in your own code; patch/fork this helper if you depend on it."],"tags":["go","panic","zip","file-descriptor","cleanup"],"backgroundTag":null,"analyzedSha":"5b18223ca19ff50d707f35410dbc6bd440a9f74d","analyzedAt":"2026-08-15T10:06:51.428Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}