{"record":{"id":"9e3049e42cbc1926","repo":"wailsapp/wails","slug":"failed-to-create-null-brush","errorCode":null,"errorMessage":"Failed to create null brush","messagePattern":"Failed to create null brush","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v2/internal/frontend/desktop/windows/winc/brush.go","lineNumber":55,"sourceCode":"\t}\n\treturn &Brush{hBrush, lb}\n}\n\nfunc NewHatchedColorBrush(color Color) *Brush {\n\tlb := w32.LOGBRUSH{LbStyle: w32.BS_HATCHED, LbColor: w32.COLORREF(color)}\n\thBrush := w32.CreateBrushIndirect(&lb)\n\tif hBrush == 0 {\n\t\tpanic(\"Faild to create solid color brush\")\n\t}\n\n\treturn &Brush{hBrush, lb}\n}\n\nfunc NewNullBrush() *Brush {\n\tlb := w32.LOGBRUSH{LbStyle: w32.BS_NULL}\n\thBrush := w32.CreateBrushIndirect(&lb)\n\tif hBrush == 0 {\n\t\tpanic(\"Failed to create null brush\")\n\t}\n\n\treturn &Brush{hBrush, lb}\n}\n\nfunc (br *Brush) GetHBRUSH() w32.HBRUSH {\n\treturn br.hBrush\n}\n\nfunc (br *Brush) GetLOGBRUSH() *w32.LOGBRUSH {\n\treturn &br.logBrush\n}\n\nfunc (br *Brush) Dispose() {\n\tif br.hBrush != 0 {\n\t\tw32.DeleteObject(w32.HGDIOBJ(br.hBrush))\n\t\tbr.hBrush = 0\n\t}","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v2/internal/frontend/desktop/windows/winc/brush.go#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat this as a symptom: profile GDI objects (Task Manager, GDIView) to find the real leak consuming handles","Check for third-party DLLs/hooks injected into the process that allocate GDI handles at startup","Ensure Pen/Brush/Font/Bitmap objects created via winc or direct w32 calls are all paired with DeleteObject/DeleteDC"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// NewNullBrush runs at package init (nullBrush var); you cannot pre-check it.\n// Prevent instead: keep the GDI handle count low before importing heavy UI packages.\n// Diagnostic check you can run at app start:\nif h := w32.GetGuiResources(w32.GetCurrentProcess(), 0 /*GR_GDIOBJECTS*/); h > 9000 {\n    log.Printf(\"GDI handles at %d — leak before UI init\", h)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat this init-time panic as evidence of a pre-existing GDI leak in the process","Audit all cgo/native dependencies that allocate GDI objects before your UI starts"],"tags":["windows","gdi","resource-leak","winc","panic","init-time"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}