wailsapp/wails · error
Cannot create canvas from invalid HDC.
Error message
Cannot create canvas from invalid HDC.
What it means
NewCanvasFromHDC is a thin wrapper that rejects a NULL HDC argument with a panic; it performs no Win32 call at all. The error is purely a caller bug: an external HDC (typically obtained from WM_PAINT's BeginPaint or a parent's GetDC) was 0/invalid before being passed in. Because doNotDispose is set, winc never releases this DC, so the caller keeps ownership.
Source
Thrown at v2/internal/frontend/desktop/windows/winc/canvas.go:35
hwnd w32.HWND
hdc w32.HDC
doNotDispose bool
}
var nullBrush = NewNullBrush()
func NewCanvasFromHwnd(hwnd w32.HWND) *Canvas {
hdc := w32.GetDC(hwnd)
if hdc == 0 {
panic(fmt.Sprintf("Create canvas from %v failed.", hwnd))
}
return &Canvas{hwnd: hwnd, hdc: hdc, doNotDispose: false}
}
func NewCanvasFromHDC(hdc w32.HDC) *Canvas {
if hdc == 0 {
panic("Cannot create canvas from invalid HDC.")
}
return &Canvas{hdc: hdc, doNotDispose: true}
}
func (ca *Canvas) Dispose() {
if !ca.doNotDispose && ca.hdc != 0 {
if ca.hwnd == 0 {
w32.DeleteDC(ca.hdc)
} else {
w32.ReleaseDC(ca.hwnd, ca.hdc)
}
ca.hdc = 0
}
}
func (ca *Canvas) DrawBitmap(bmp *Bitmap, x, y int) {View on GitHub (pinned to 0e754b1b40)
Solutions
- Check the HDC for 0 before constructing the canvas (see validation code) and skip/handle the paint
- Fix the upstream call that produced the NULL HDC: verify BeginPaint/GetDC/GetWindowDC succeeded and that the target HWND is valid
- Never cache the HDC across messages — get it fresh each WM_PAINT and wrap it immediately
Example fix
// before
hdc := w32.BeginPaint(hwnd, &ps)
ca := winc.NewCanvasFromHDC(hdc) // panics when BeginPaint returned 0
// after
hdc := w32.BeginPaint(hwnd, &ps)
if hdc == 0 { return 0 }
ca := winc.NewCanvasFromHDC(hdc) Defensive patterns
Strategy: validation
Validate before calling
if hdc != 0 {
ca := winc.NewCanvasFromHDC(hdc)
defer func() { /* canvas will not release it; owner releases via EndPaint/ReleaseDC */ }()
_ = ca
} Prevention
- Never construct the canvas before verifying the HDC source call succeeded
- Remember doNotDispose=true: the caller owns release (EndPaint/ReleaseDC)
When it happens
Trigger: Passing an HDC that a prior API returned NULL for — e.g. w32.BeginPaint failed, GetDC(hwnd) returned 0, or a bit-blit helper handed back 0 on failure; storing an HDC after its owning DC was deleted and reusing it.
Common situations: Custom WM_PAINT handling in winc controls where the developer extracts hdc from w32.PAINTSTRUCT without checking BeginPaint's return; interop code receiving an HDC over FFI/channel that can legitimately be 0.
Related errors
- GetSysColorBrush failed
- Create canvas from %v failed.
- Invalid font style
- Faild to create solid color brush
- Failed to create null brush
AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15).
Data as JSON: /api/errors/01d405e30b4c974a.
Report an issue: GitHub.