fyne-io/fyne · critical

Run() or ShowAndRun() must be called from main goroutine

Error message

Run() or ShowAndRun() must be called from main goroutine

What it means

The glfw desktop driver's Run() must execute on the process's main goroutine: macOS/Cocoa requires the event loop on the main thread, and gLDriver.Run enforces it with an async.IsMainGoroutine() check before doing anything. The panic is deliberate - continuing would deadlock or abort deeper inside the windowing layer.

Source

Thrown at internal/driver/glfw/driver.go:192

}

func (d *gLDriver) windowList() []fyne.Window {
	return d.windows
}

func (d *gLDriver) initFailed(msg string, err error) {
	fyne.LogError(msg, err)

	if running.Load() {
		os.Exit(1) //revive:disable-line:deep-exit
	}

	d.Quit()
}

func (d *gLDriver) Run() {
	if !async.IsMainGoroutine() {
		panic("Run() or ShowAndRun() must be called from main goroutine")
	}

	go d.catchTerm()
	d.runGL()

	// Ensure lifecycle events run to completion before the app exits
	l := fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle)
	l.WaitForEvents()
	l.DestroyEventQueue()
}

func (*gLDriver) SetDisableScreenBlanking(disable bool) {
	setDisableScreenBlank(disable)
}

// NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings.
func NewGLDriver() fyne.Driver {
	repository.Register(fyne.URISchemeFile, intRepo.NewFileRepository())

View on GitHub (pinned to 8860ee95c3)

Solutions

  1. Call Run()/ShowAndRun() synchronously at the end of func main() - never wrap it in 'go'
  2. Do slow initialization in goroutines before or beside the event loop; only the Run call itself stays on main
  3. In tests, use the fyne test driver instead of starting the glfw event loop

Example fix

// before
func main() {
    a := app.New()
    w := a.NewWindow("Hi")
    go w.ShowAndRun() // panics: not the main goroutine
    select {}
}

// after
func main() {
    a := app.New()
    w := a.NewWindow("Hi")
    w.ShowAndRun() // runs on main, blocks until quit
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Invoking app.Run() or window.ShowAndRun() from a non-main goroutine: 'go func() { ... w.ShowAndRun() }()', launching the UI from a server goroutine, or calling it from a callback that another library runs in its own goroutine.

Common situations: Wrapping startup in 'go' to 'not block' main; starting the GUI from an HTTP handler or gRPC server loop; refactors that moved the entry point into a helper called concurrently; some test harnesses that spawn the UI.

Related errors


AI-assisted analysis of fyne-io/fyne@8860ee95c3 (2026-08-15). Data as JSON: /api/errors/74a415d30175ba02. Report an issue: GitHub.