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

  1. Create all winc controls on the main/UI thread before the owning Form is closed; never race control creation against WM_DESTROY
  2. Verify the parent controller's Handle() is a live window (w32.IsWindow) before constructing the child
  3. For common-controls classes, ensure InitCommonControlsEx is called and the application manifest requests comctl32 v6 (Windows then provides themed Sys* classes)
  4. 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

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.