wailsapp/wails · error
initMainLoop was not called
Error message
initMainLoop was not called
What it means
On Windows, windowsApp.invokeRequired() compares the current thread ID against the main thread ID captured during main-loop initialization; if mainThreadID is still 0 it panics with 'initMainLoop was not called'. It means UI-thread affinity logic ran before the application main loop was initialized.
Source
Thrown at v3/pkg/application/mainthread_windows.go:96
if m.invokeRequired() {
w32.PostMessage(mainThreadHWND, wmInvokeCallback, uintptr(id), 0)
} else {
mainThreadFunctionStoreLock.Lock()
fn := mainThreadFunctionStore[id]
delete(mainThreadFunctionStore, id)
mainThreadFunctionStoreLock.Unlock()
if fn == nil {
Fatal("dispatchOnMainThread called with invalid id: %v", id)
}
fn()
}
}
func (m *windowsApp) invokeRequired() bool {
mainThreadID := m.mainThreadID
if mainThreadID == 0 {
panic("initMainLoop was not called")
}
return mainThreadID != w32.GetCurrentThreadId()
}
func (m *windowsApp) invokeCallback(wParam, lParam uintptr) {
// TODO: Should we invoke just one or all queued? In v2 we always invoked all pendings...
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if m.invokeRequired() {
panic("invokeCallback must always be called on the MainOSThread")
}
mainThreadFunctionStoreLock.Lock()
fnIDs := make([]uint, 0, len(mainThreadFunctionStore))
for id := range mainThreadFunctionStore {
fnIDs = append(fnIDs, id)
}View on GitHub (pinned to 0e754b1b40)
Solutions
- Defer any UI or dispatch calls until after the application main loop is running (inside service Startup hooks or later)
- In tests, initialize the main loop / use the app test harness instead of calling windows UI helpers directly
- Review startup ordering so nothing touches invokeRequired before initMainLoop executes
Example fix
// before
func NewApp() *App {
a := &App{}
showNotification() // dispatches before main loop: panics
return a
}
// after
func (a *App) Startup(ctx context.Context) {
showNotification() // main loop running: safe
} Defensive patterns
Strategy: validation
Validate before calling
// only dispatch UI work once the app is running
if appStarted.Load() {
app.DispatchOnMainThread(fn)
} else {
queueUntilStartup(fn) // drain inside your Startup hook
} Prevention
- Do not call window/dialog/notification APIs from init() or constructors
- Move UI work into the service Startup hook or later lifecycle callbacks
- In tests, boot the app test harness before touching Windows UI helpers
When it happens
Trigger: Any code path that reaches invokeRequired() (e.g. invokeCallback or dispatch machinery) before the app's main loop initialized the main thread ID — typically invoking/dispatching work before app.Run() has started the main loop, or using windowing APIs in unit tests that never start the app.
Common situations: Calling dialog/notification/window helpers from an init() or from a service constructor that runs before the app starts; tests exercising Windows app code without booting the main loop; ordering changes in startup code after a v3 upgrade.
Related errors
- ${await response.text()}
- no focused element to type into; pass a selector or click a
- SendMessage(LVM_SETCALLBACKMASK)
- SendMessage(LVM_UPDATE)
- use ShowAndGetResults for open multiple files dialog
AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15).
Data as JSON: /api/errors/fb3fba97b6823522.
Report an issue: GitHub.