{"record":{"id":"b784956ce5e560b0","repo":"wailsapp/wails","slug":"createfontindirect-failed","errorCode":null,"errorMessage":"CreateFontIndirect failed","messagePattern":"CreateFontIndirect failed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v2/internal/frontend/desktop/windows/winc/font.go","lineNumber":52,"sourceCode":"func NewFont(family string, pointSize int, style byte) *Font {\n\tif style > FontBold|FontItalic|FontUnderline|FontStrikeOut {\n\t\tpanic(\"Invalid font style\")\n\t}\n\n\t//Retrive screen DPI\n\thDC := w32.GetDC(0)\n\tdefer w32.ReleaseDC(0, hDC)\n\tscreenDPIY := w32.GetDeviceCaps(hDC, w32.LOGPIXELSY)\n\n\tfont := Font{\n\t\tfamily:    family,\n\t\tpointSize: pointSize,\n\t\tstyle:     style,\n\t}\n\n\tfont.hfont = font.createForDPI(screenDPIY)\n\tif font.hfont == 0 {\n\t\tpanic(\"CreateFontIndirect failed\")\n\t}\n\n\treturn &font\n}\n\nfunc (fnt *Font) createForDPI(dpi int) w32.HFONT {\n\tvar lf w32.LOGFONT\n\n\tlf.Height = int32(-w32.MulDiv(fnt.pointSize, dpi, 72))\n\tif fnt.style&FontBold > 0 {\n\t\tlf.Weight = w32.FW_BOLD\n\t} else {\n\t\tlf.Weight = w32.FW_NORMAL\n\t}\n\tif fnt.style&FontItalic > 0 {\n\t\tlf.Italic = 1\n\t}\n\tif fnt.style&FontUnderline > 0 {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v2/internal/frontend/desktop/windows/winc/font.go#L34-L70","documentation":"After validating the style, NewFont measures screen DPI, builds a LOGFONT (height = -MulDiv(pointSize, dpi, 72), weight/family/italic from style) and calls CreateFontIndirect via createForDPI; it panics when the returned HFONT is NULL. CreateFontIndirect fails only under GDI handle exhaustion or with a malformed LOGFONT; any family string is accepted (Windows performs font substitution), so the family name itself is not the trigger.","triggerScenarios":"Creating Font objects per-control/per-repaint and never deleting the HFONTs until the 10000 GDI handle quota is reached; creating fonts during shutdown while GDI is unavailable.","commonSituations":"Apps applying fonts in loops (e.g. styling every list item with a fresh NewFont); the DefaultFont-style package var pattern broken by per-call creation; GDI leak elsewhere in the process.","solutions":["Cache fonts: create them once (package-level or per-window) and reuse across controls","Delete HFONTs of replaced fonts with w32.DeleteObject when a control's font is swapped","Watch GDI object count in Task Manager to confirm exhaustion; find the true leak with GDIView"],"exampleFix":"// before\nfor _, row := range rows {\n    row.SetFont(winc.NewFont(\"MS Shell Dlg 2\", 8, 0)) // new HFONT per row -> exhaustion -> panic\n}\n\n// after\nvar uiFont = winc.NewFont(\"MS Shell Dlg 2\", 8, 0) // once\nfor _, row := range rows {\n    row.SetFont(uiFont)\n}","handlingStrategy":"validation","validationCode":"// Cache fonts; only create when the GDI budget allows.\nvar fontCache = map[fontKey]*winc.Font{}\n\ntype fontKey struct { fam string; size int; style byte }\n\nfunc getFont(fam string, size int, style byte) *winc.Font {\n    k := fontKey{fam, size, style}\n    if f, ok := fontCache[k]; ok { return f }\n    f := winc.NewFont(fam, size, style)\n    fontCache[k] = f\n    return f\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One Font instance per distinct style, created once","Delete replaced HFONTs with w32.DeleteObject","Monitor GDI objects during development"],"tags":["windows","gdi","font","resource-leak","winc","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}