coreybutler/nvm-windows · error
panic(err)
Error message
panic(err)
What it means
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).
Source
Thrown at src/web/web.go:451
}
}
// Check online to see if a 64 bit version exists
_, err := client.Head(url)
if err != nil {
return ""
}
return url
}
func unzip(src, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
os.MkdirAll(dest, 0755)
// Closure to address file descriptors issue with all the deferred .Close() methods
extractAndWriteFile := func(f *zip.File) error {
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
path := filepath.Join(dest, f.Name)View on GitHub (pinned to 5b18223ca1)
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.
Example fix
// before
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
// after
defer func() {
if err := r.Close(); err != nil {
// close failure after a successful read is non-fatal; surface it, don't crash
log.Printf("unzip: closing archive %s: %v", src, err)
}
}() Defensive patterns
Strategy: try-catch
Validate before calling
// Cheap pre-checks before unzipping: archive exists, is readable, and opens as a zip
func canUnzip(src string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
if fi.Size() == 0 {
return fmt.Errorf("archive %s is empty (truncated download?)", src)
}
r, err := zip.OpenReader(src)
if err != nil {
return err
}
return r.Close() // also exercises the close path once, up front
} Try / catch
// Go: recover panics at the API boundary so a close failure degrades, not crashes
func safeUnzip(src, dest string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("unzip %s: panic: %v", src, r)
}
}()
return unzip(src, dest)
} Prevention
- 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.
When it happens
Trigger: 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().
Common situations: 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.
Related errors
AI-assisted analysis of coreybutler/nvm-windows@5b18223ca1 (2026-08-15).
Data as JSON: /api/errors/ee1a61dd52076351.
Report an issue: GitHub.