wailsapp/wails · critical
failed to load gdi32.dll: %v
Error message
failed to load gdi32.dll: %v
What it means
The same icon.go init that loads user32.dll also loads gdi32.dll and panics with the wrapped error if it fails. gdi32.dll is a permanent system DLL used for all drawing (icon bitmaps, DCs, brushes), so a load failure is environmental, not a code bug: blocked by security software, missing in stripped-down Windows containers/server-core-without-GUI, or an emulation layer without a matching-bitness gdi32.
Source
Thrown at v2/internal/frontend/desktop/windows/winc/icon.go:40
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
BiWidth int32
BiHeight int32
BiPlanes uint16View on GitHub (pinned to 0e754b1b40)
Solutions
- Keep GUI dependencies (winc imports) out of headless services/containers — split them behind a build tag or separate package so init never runs there
- Read the wrapped %v error: access-denied -> policy/AV; module-not-found -> missing DLL on that SKU
- On stripped SKUs, switch to a full desktop or servercore image that includes gdi32.dll
- Verify with `dumpbin /dependents` (or Dependency Walker) that the exe's DLL expectations match the host
Example fix
// before: headless service transitively imports winc -> init panics without gdi32 import _ "myapp/guipanel" // after: gate GUI code behind a build tag //go:build windows && gui package guipanel
Defensive patterns
Strategy: fallback
Validate before calling
// As with user32: pre-flight the environment outside the process, or gate imports. //go:build windows && gui // package gui // imports winc icon package only in GUI builds
Prevention
- Use full desktop/servercore container images when GUI deps are needed
- Split headless services from GUI code with build tags
- Verify DLL presence in the deploy image before shipping
When it happens
Trigger: Importing the winc icon package (any transitively importing package triggers init) on headless Windows containers; 64-bit process under emulation with only 32-bit gdi32; DLL load blocked by AppLocker/EDR; corrupted system DLL search path.
Common situations: Docker 'windows' container images based on servercore:nanoserver missing GUI DLLs when a GUI dependency sneaks into a service build; CI agents without interactive desktops when tests import winc.
Related errors
- failed to load user32.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/12c322ebec785c00.
Report an issue: GitHub.