{"record":{"id":"74a415d30175ba02","repo":"fyne-io/fyne","slug":"run-or-showandrun-must-be-called-from-main-gor","errorCode":null,"errorMessage":"Run() or ShowAndRun() must be called from main goroutine","messagePattern":"Run\\(\\) or ShowAndRun\\(\\) must be called from main goroutine","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/driver/glfw/driver.go","lineNumber":192,"sourceCode":"}\n\nfunc (d *gLDriver) windowList() []fyne.Window {\n\treturn d.windows\n}\n\nfunc (d *gLDriver) initFailed(msg string, err error) {\n\tfyne.LogError(msg, err)\n\n\tif running.Load() {\n\t\tos.Exit(1) //revive:disable-line:deep-exit\n\t}\n\n\td.Quit()\n}\n\nfunc (d *gLDriver) Run() {\n\tif !async.IsMainGoroutine() {\n\t\tpanic(\"Run() or ShowAndRun() must be called from main goroutine\")\n\t}\n\n\tgo d.catchTerm()\n\td.runGL()\n\n\t// Ensure lifecycle events run to completion before the app exits\n\tl := fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle)\n\tl.WaitForEvents()\n\tl.DestroyEventQueue()\n}\n\nfunc (*gLDriver) SetDisableScreenBlanking(disable bool) {\n\tsetDisableScreenBlank(disable)\n}\n\n// NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings.\nfunc NewGLDriver() fyne.Driver {\n\trepository.Register(fyne.URISchemeFile, intRepo.NewFileRepository())","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/fyne-io/fyne/blob/8860ee95c356385effa5a7be51adb11c6be9d2b6/internal/driver/glfw/driver.go#L174-L210","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call Run()/ShowAndRun() synchronously at the end of func main() - never wrap it in 'go'","Do slow initialization in goroutines before or beside the event loop; only the Run call itself stays on main","In tests, use the fyne test driver instead of starting the glfw event loop"],"exampleFix":"// before\nfunc main() {\n    a := app.New()\n    w := a.NewWindow(\"Hi\")\n    go w.ShowAndRun() // panics: not the main goroutine\n    select {}\n}\n\n// after\nfunc main() {\n    a := app.New()\n    w := a.NewWindow(\"Hi\")\n    w.ShowAndRun() // runs on main, blocks until quit\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep Run()/ShowAndRun() as the last statement of func main() - never inside 'go'","Grep for 'go .*ShowAndRun' and 'go .*\\.Run()' when a run suddenly panics","Push slow startup work into goroutines that finish before the event loop, not around it","Use the fyne test driver in unit tests instead of starting glfw"],"tags":["gui","glfw","main-goroutine","concurrency","runtime","go"],"backgroundTag":null,"analyzedSha":"8860ee95c356385effa5a7be51adb11c6be9d2b6","analyzedAt":"2026-08-15T22:01:51.624Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}