wailsapp/wails · error

Failed to create null brush

Error message

Failed to create null brush

What it means

NewNullBrush creates a BS_NULL brush via CreateBrushIndirect and panics if the handle is NULL. A null brush is used for transparent fill; the package-level variable nullBrush in canvas.go means this runs once at init. Failure of CreateBrushIndirect for BS_NULL is only expected under GDI handle exhaustion or during process teardown, so hitting this panic almost always means the process has leaked ~10000 GDI objects.

Source

Thrown at v2/internal/frontend/desktop/windows/winc/brush.go:55

	}
	return &Brush{hBrush, lb}
}

func NewHatchedColorBrush(color Color) *Brush {
	lb := w32.LOGBRUSH{LbStyle: w32.BS_HATCHED, LbColor: w32.COLORREF(color)}
	hBrush := w32.CreateBrushIndirect(&lb)
	if hBrush == 0 {
		panic("Faild to create solid color brush")
	}

	return &Brush{hBrush, lb}
}

func NewNullBrush() *Brush {
	lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL}
	hBrush := w32.CreateBrushIndirect(&lb)
	if hBrush == 0 {
		panic("Failed to create null brush")
	}

	return &Brush{hBrush, lb}
}

func (br *Brush) GetHBRUSH() w32.HBRUSH {
	return br.hBrush
}

func (br *Brush) GetLOGBRUSH() *w32.LOGBRUSH {
	return &br.logBrush
}

func (br *Brush) Dispose() {
	if br.hBrush != 0 {
		w32.DeleteObject(w32.HGDIOBJ(br.hBrush))
		br.hBrush = 0
	}

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Treat this as a symptom: profile GDI objects (Task Manager, GDIView) to find the real leak consuming handles
  2. Check for third-party DLLs/hooks injected into the process that allocate GDI handles at startup
  3. Ensure Pen/Brush/Font/Bitmap objects created via winc or direct w32 calls are all paired with DeleteObject/DeleteDC
Defensive patterns

Strategy: validation

Validate before calling

// NewNullBrush runs at package init (nullBrush var); you cannot pre-check it.
// Prevent instead: keep the GDI handle count low before importing heavy UI packages.
// Diagnostic check you can run at app start:
if h := w32.GetGuiResources(w32.GetCurrentProcess(), 0 /*GR_GDIOBJECTS*/); h > 9000 {
    log.Printf("GDI handles at %d — leak before UI init", h)
}

Prevention

When it happens

Trigger: GDI handle table full when NewNullBrush is first called (i.e. the leak happened before this package initialized); GDI already invalidated during shutdown-time use.

Common situations: Rarely seen because it fires at package init; when it does, another library or a cgo dependency has already consumed the GDI quota before winc's canvas package initialized.

Related errors


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