coreybutler/nvm-windows · critical

illegal file path: %s

Error message

illegal file path: %s

What it means

During nvm-windows' unzip of a downloaded node archive, each entry's target path is checked against filepath.Clean(dest)+separator. This 'illegal file path' error is the ZipSlip guard firing: the archive contains an entry whose Name resolves OUTSIDE the intended destination (via ../ segments or absolute paths), and nvm refuses to extract it as a security measure.

Source

Thrown at src/web/web.go:473

	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)

		// Check for ZipSlip (Directory traversal)
		if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
			return fmt.Errorf("illegal file path: %s", path)
		}

		if f.FileInfo().IsDir() {
			os.MkdirAll(path, f.Mode())
		} else {
			os.MkdirAll(filepath.Dir(path), f.Mode())
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer func() {
				if err := f.Close(); err != nil {
					panic(err)
				}
			}()

			_, err = io.Copy(f, rc)
			if err != nil {

View on GitHub (pinned to 5b18223ca1)

Solutions

  1. Immediately switch back to the official mirror (unset NVM_NODE_MIRROR / point to https://nodejs.org/dist) — a ZipSlip hit strongly suggests a tampered or broken mirror.
  2. Delete the cached downloaded zip so nvm re-downloads from the good mirror.
  3. Verify the archive manually: download it, list entries (e.g. via a zip tool), and confirm no entry escapes the root.
  4. Check the mirror URL for typos in settings.txt (node_mirror / npm_mirror).

Example fix

// before
path := filepath.Join(dest, f.Name)
if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
    return fmt.Errorf("illegal file path: %s", path)
}

// after: same guard, but name the attack class to guide users to the mirror fix
path := filepath.Join(dest, f.Name)
if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
    return fmt.Errorf("zipslip detected: archive entry %q escapes destination %s — the download is likely corrupted or from a tampered mirror; re-download from an official mirror", f.Name, dest)
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate every zip entry stays under dest before extracting
func zipSafe(r *zip.ReadCloser, dest string) error {
    target := filepath.Clean(dest) + string(os.PathSeparator)
    for _, f := range r.File {
        p := filepath.Clean(filepath.Join(dest, f.Name))
        if !strings.HasPrefix(p, target) || filepath.IsAbs(f.Name) {
            return fmt.Errorf("unsafe entry %q", f.Name)
        }
    }
    return nil
}

Try / catch

if err := unzip(src, dest); err != nil {
    if strings.Contains(err.Error(), "illegal file path") {
        os.Remove(src) // discard suspect archive
        return errors.New("download failed integrity check — switched to official mirror required")
    }
}

Prevention

When it happens

Trigger: Downloading a corrupted or tampered node archive (mirror serving a malicious/rewritten zip), or an archive with entry names like '../../windows/system32/x' or 'C:\Windows\...'. The HasPrefix check fails and extraction aborts.

Common situations: Third-party/unofficial mirrors serving repackaged archives; corrupted downloads where entry names are garbage; extremely rare with official nodejs.org — almost always a bad mirror URL.

Related errors


AI-assisted analysis of coreybutler/nvm-windows@5b18223ca1 (2026-08-15). Data as JSON: /api/errors/f441480ee6feb0d8. Report an issue: GitHub.