wailsapp/wails · critical

failed to load user32.dll: %v

Error message

failed to load user32.dll: %v

What it means

icon.go's package init eagerly calls user32.Load() and panics with the wrapped error if user32.dll cannot be loaded, surfacing missing system APIs before any icon function runs. user32.dll is a permanent Windows system DLL, so failure means the process environment itself is broken: the DLL is blocked by policy/security software, the image is being loaded in an unusual host (server core without interactive session machinery, wine/containers missing it), or API sets are unresolved in exotic loader contexts.

Source

Thrown at v2/internal/frontend/desktop/windows/winc/icon.go:37

	"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
)

var (
	user32                 = syscall.NewLazyDLL("user32.dll")
	gdi32                  = syscall.NewLazyDLL("gdi32.dll")
	procGetIconInfo        = user32.NewProc("GetIconInfo")
	procDeleteObject       = gdi32.NewProc("DeleteObject")
	procGetObject          = gdi32.NewProc("GetObjectW")
	procGetDIBits          = gdi32.NewProc("GetDIBits")
	procCreateCompatibleDC = gdi32.NewProc("CreateCompatibleDC")
	procSelectObject       = gdi32.NewProc("SelectObject")
	procDeleteDC           = gdi32.NewProc("DeleteDC")
)

func init() {
	// Validate DLL loads at initialization time to surface missing APIs early
	if err := user32.Load(); err != nil {
		panic(fmt.Sprintf("failed to load user32.dll: %v", err))
	}
	if err := gdi32.Load(); err != nil {
		panic(fmt.Sprintf("failed to load gdi32.dll: %v", err))
	}
}

// ICONINFO mirrors the Win32 ICONINFO struct
type ICONINFO struct {
	FIcon    int32
	XHotspot uint32
	YHotspot uint32
	HbmMask  uintptr
	HbmColor uintptr
}

// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx
type BITMAPINFOHEADER struct {
	BiSize          uint32

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Run the GUI build only on desktop Windows SKUs with an interactive session; for headless CI, gate tests that import winc with build tags or skip on non-interactive sessions
  2. Check the wrapped error text (%v) — ERROR_ACCESS_DENIED points to AV/EDR policy, ERROR_MOD_NOT_FOUND points to a missing/blocked system DLL
  3. Remove any user32.dll shadow copies from the application directory and verify C:\Windows\System32\user32.dll exists and loads (rundll32 sanity check)
  4. If running under Wine/Proton, ensure a 64-bit windows user32 is present for a 64-bit build
Defensive patterns

Strategy: fallback

Validate before calling

// init() runs on import; you cannot catch it in-process. Pre-flight the host instead:
// (run in a launcher script or a tiny bootstrap exe before the GUI binary)
//   if not exist %WINDIR%\System32\user32.dll exit /b 1

Prevention

When it happens

Trigger: Importing the winc icon package on a Windows SKU where user32 is unavailable/blocked (locked-down server core, sandbox); antivirus/EDR blocking DLL load; running under an emulation layer missing user32; corruption of the system DLL search path (SAFESEARCH mode with a rogue user32.dll nearby).

Common situations: CI or container images attempting to run/test a GUI build headless; hardened kiosk/terminal servers; a fake user32.dll shadowing the real one in the app directory.

Related errors


AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15). Data as JSON: /api/errors/8a570ed0a4f2a097. Report an issue: GitHub.