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 uint32View on GitHub (pinned to 0e754b1b40)
Solutions
- 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
- 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
- Remove any user32.dll shadow copies from the application directory and verify C:\Windows\System32\user32.dll exists and loads (rundll32 sanity check)
- 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
- Deploy GUI builds only on desktop Windows with an interactive session
- Keep winc imports behind a 'gui' build tag so headless builds never trigger init
- Check the wrapped error for access-denied (policy) vs module-not-found (missing DLL)
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
- failed to load gdi32.dll: %v
- Failed to create null brush
- Faild to create solid color brush
- GetSysColorBrush failed
- missing icon with icon ID: %d
AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15).
Data as JSON: /api/errors/8a570ed0a4f2a097.
Report an issue: GitHub.