wailsapp/wails · critical
cannot create window for " + className
Error message
cannot create window for " + className
What it means
ControlBase.InitControl creates a child window with the given Win32 class name (BUTTON, EDIT, SysListView32, ...) via CreateWindow and panics when the handle is NULL. For stock classes the class is always registered by Windows, so failure comes from the arguments/environment: an invalid parent HWND, styles that are illegal for a child, or the class not being registered (unicode/manifest issues are rare). InitControl is used by every winc control constructor, so this panic surfaces at NewButton/NewEditText/... time.
Source
Thrown at v2/internal/frontend/desktop/windows/winc/controlbase.go:72
onMouseHover EventManager
onMouseLeave EventManager
// Keyboard events
onKeyUp EventManager
// Paint events
onPaint EventManager
onSize EventManager
m sync.Mutex
dispatchq []func()
}
// InitControl is called by controls: edit, button, treeview, listview, and so on.
func (cba *ControlBase) InitControl(className string, parent Controller, exstyle, style uint) {
cba.hwnd = CreateWindow(className, parent, exstyle, style)
if cba.hwnd == 0 {
panic("cannot create window for " + className)
}
cba.parent = parent
}
// InitWindow is called by custom window based controls such as split, panel, etc.
func (cba *ControlBase) InitWindow(className string, parent Controller, exstyle, style uint) {
RegClassOnlyOnce(className)
cba.hwnd = CreateWindow(className, parent, exstyle, style)
if cba.hwnd == 0 {
panic("cannot create window for " + className)
}
cba.parent = parent
}
// SetTheme for TreeView and ListView controls.
func (cba *ControlBase) SetTheme(appName string) error {
if hr := w32.SetWindowTheme(cba.hwnd, syscall.StringToUTF16Ptr(appName), nil); w32.FAILED(hr) {
return fmt.Errorf("SetWindowTheme %d", hr)View on GitHub (pinned to 0e754b1b40)
Solutions
- Create all winc controls on the main/UI thread before the owning Form is closed; never race control creation against WM_DESTROY
- Verify the parent controller's Handle() is a live window (w32.IsWindow) before constructing the child
- For common-controls classes, ensure InitCommonControlsEx is called and the application manifest requests comctl32 v6 (Windows then provides themed Sys* classes)
- Call GetLastError via w32 after a failed CreateWindow (temporarily wrap CreateWindow) to get the exact Win32 reason (ERROR_INVALID_WINDOW_HANDLE=1400, ERROR_CANNOT_FIND_WND_CLASS=1407, ...)
Example fix
// before
go func() {
btn := winc.NewPushButton(form) // goroutine may run after form destroyed -> CreateWindow returns 0 -> panic
}()
// after
form.Synchronize(func() { // run on the UI thread while the form is alive
if w32.IsWindow(form.Handle()) {
_ = winc.NewPushButton(form)
}
}) Defensive patterns
Strategy: validation
Validate before calling
func isCreatableParent(p winc.Controller) bool {
return p != nil && p.Handle() != 0 && w32.IsWindow(p.Handle())
}
if isCreatableParent(parent) {
btn := winc.NewPushButton(parent)
} Prevention
- Create all controls synchronously on the UI thread during form setup
- Never let goroutines race window destruction with control creation
- Ensure a comctl32 v6 manifest and InitCommonControlsEx for common-control classes
When it happens
Trigger: Constructing a winc control with a parent whose HWND is 0 or already destroyed (e.g. parent Form closed before child creation goroutine ran); passing style flags that combine incompatible WS_/BS_ bits; calling control constructors from a non-main thread while the message loop is torn down; a comctl32-dependent class (e.g. SysLink) not available without InitCommonControlsEx / a v6 common-controls manifest.
Common situations: Goroutines that create controls after the window was destroyed at app exit; missing comctl32 v6 manifest in a hand-built exe causing newer control classes to fail; passing winc.NoParent-like zero Controller where a real parent is required.
Related errors
AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15).
Data as JSON: /api/errors/b8ee1519c0ec0e4a.
Report an issue: GitHub.