coreybutler/nvm-windows · warning

failed to set hidden attribute: %w

Error message

failed to set hidden attribute: %w

What it means

This error is returned when nvm-windows' upgrade routine fails to mark a file as hidden via the Win32 API SetFileAttributesW. The syscall's return value is 0, indicating the attribute was not applied, and the wrapped syscall error (often 'The operation completed successfully' from GetProcAddress-style calls) carries the real reason. Typical causes are missing permissions on the target file, the file being locked by another process, or the path pointer being invalid.

Source

Thrown at src/upgrade/upgrade.go:1075

func setHidden(path string) error {
	// Convert the path to a UTF-16 encoded string
	lpFileName, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return fmt.Errorf("failed to encode path: %w", err)
	}

	// Call the Windows API function
	ret, _, err := syscall.NewLazyDLL("kernel32.dll").
		NewProc("SetFileAttributesW").
		Call(
			uintptr(unsafe.Pointer(lpFileName)),
			uintptr(FILE_ATTRIBUTE_HIDDEN),
		)

	// Check the result
	if ret == 0 {
		return fmt.Errorf("failed to set hidden attribute: %w", err)
	}
	return nil
}

View on GitHub (pinned to 5b18223ca1)

Solutions

  1. Close all terminals/processes using nvm.exe so the file is not locked, then retry the upgrade.
  2. Run the upgrade from an elevated (Administrator) prompt so SetFileAttributesW has permission.
  3. Verify the file path passed as lpFileName exists and is correctly UTF-16 encoded before the Call.
  4. Check the wrapped 'err' value in the message: a real errno (e.g. ERROR_ACCESS_DENIED) points to permissions; 'operation completed successfully' means ret==0 spuriously — re-check the path argument.

Example fix

// before
ret, _, err := syscall.NewLazyDLL("kernel32.dll").NewProc("SetFileAttributesW").Call(uintptr(unsafe.Pointer(lpFileName)), uintptr(FILE_ATTRIBUTE_HIDDEN))
if ret == 0 {
    return fmt.Errorf("failed to set hidden attribute: %w", err)
}

// after: surface the Win32 error code when ret == 0 and err is the 'success' placeholder
if ret == 0 {
    if err == syscall.Errno(0) {
        return fmt.Errorf("failed to set hidden attribute on %s: SetFileAttributesW returned 0 (file locked or inaccessible)", fileName)
    }
    return fmt.Errorf("failed to set hidden attribute on %s: %w", fileName, err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Before upgrading / hiding files, confirm the target is not locked and exists
if _, err := os.Stat(fileName); err != nil {
    return fmt.Errorf("cannot hide %s: %w", fileName, err)
}
// Ensure no running process holds the binary (best-effort on Windows)
cmd := exec.Command("tasklist", "/FI", fmt.Sprintf("IMAGENAME eq %s", filepath.Base(fileName)))
out, _ := cmd.Output()
if !strings.Contains(string(out), "No tasks") {
    return errors.New("file in use — close processes before upgrading")
}

Try / catch

err := hideFile(path)
if err != nil {
    // Non-fatal: hidden attribute is cosmetic for upgrade cleanup
    log.Printf("warning: could not hide %s: %v", path, err)
}

Prevention

When it happens

Trigger: Calling the upgrade path in src/upgrade/upgrade.go that hides the old nvm.exe (or symlink target) after an upgrade, when SetFileAttributesW returns 0: target file is in use (running nvm.exe), path does not exist, or the process lacks WriteAttributes permission on the file.

Common situations: Upgrading nvm-windows while an nvm shell/command is still open; antivirus holding the binary; running from a directory where the user lacks NTFS attribute-write rights; UAC-elevated directory but non-elevated nvm.

Related errors


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