wailsapp/wails · error

SendMessage(LVM_SETCALLBACKMASK)

Error message

SendMessage(LVM_SETCALLBACKMASK)

What it means

ListView's checkbox support (SetCheckBoxes -> the setCheckboxMask helper) reads the current callback mask with LVM_GETCALLBACKMASK, sets/clears LVIS_STATEIMAGEMASK, then sends LVM_SETCALLBACKMASK and panics if the message returns FALSE. The message fails when the HWND is not a list-view in a valid state — typically a destroyed window or a handle that never was a SysListView32 — because a live list-view accepts any callback mask.

Source

Thrown at v2/internal/frontend/desktop/windows/winc/listview.go:207

	oldStyle := exStyle
	if value {
		exStyle |= w32.LVS_EX_CHECKBOXES
	} else {
		exStyle &^= w32.LVS_EX_CHECKBOXES
	}
	if exStyle != oldStyle {
		w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, exStyle)
	}

	mask := w32.SendMessage(lv.hwnd, w32.LVM_GETCALLBACKMASK, 0, 0)
	if value {
		mask |= w32.LVIS_STATEIMAGEMASK
	} else {
		mask &^= w32.LVIS_STATEIMAGEMASK
	}

	if w32.SendMessage(lv.hwnd, w32.LVM_SETCALLBACKMASK, mask, 0) == w32.FALSE {
		panic("SendMessage(LVM_SETCALLBACKMASK)")
	}
}

func (lv *ListView) applyImage(lc *w32.LVITEM, imIndex int) {
	if lv.iml != nil {
		lc.Mask |= w32.LVIF_IMAGE
		lc.IImage = int32(imIndex)
	}
}

func (lv *ListView) AddItem(item ListItem) {
	lv.InsertItem(item, lv.ItemCount())
}

func (lv *ListView) InsertItem(item ListItem, index int) {
	text := item.Text()
	li := &w32.LVITEM{
		Mask:    w32.LVIF_TEXT | w32.LVIF_PARAM,

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Configure checkboxes once at setup, on the UI thread, while the form is guaranteed alive
  2. Guard late/async calls with w32.IsWindow(lv.Handle()) before SetCheckBoxes
  3. Cancel pending UI-mutating goroutines when the form closes (context/WaitGroup tied to WM_DESTROY)

Example fix

// before
func (f *MainForm) onSettingsChange() {
    f.list.SetCheckBoxes(true) // may run after window destroyed -> LVM_SETCALLBACKMASK FALSE -> panic
}

// after
func (f *MainForm) onSettingsChange() {
    if !w32.IsWindow(f.list.Handle()) { return }
    f.list.SetCheckBoxes(true)
}
Defensive patterns

Strategy: validation

Validate before calling

func setCheckBoxesSafe(lv *winc.ListView, on bool) {
    if lv == nil || lv.Handle() == 0 || !w32.IsWindow(lv.Handle()) { return }
    lv.SetCheckBoxes(on)
}

Prevention

When it happens

Trigger: Calling listView.SetCheckBoxes(true) after the control's window was destroyed; calling it from a goroutine racing form teardown; invoking it on a control whose creation failed so hwnd is 0; sending LVM_* to a non-listview handle via misuse of the API.

Common situations: App shutdown paths that reconfigure UI (e.g. persisting preferences triggers SetCheckBoxes) while the window is closing; deferred configuration callbacks firing after close.

Related errors


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