You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
macOS: dragging a file out of an open NSOpenPanel intermittently terminates the event loop; any [NSApp run] return is treated as exit
Environment
tao 0.35.3 (via Tauri 2.11.5, tauri-plugin-dialog 2.7.2 → rfd 0.16.0)
macOS 26.5.2 (25F84), Apple Silicon (Mac17,9)
Summary
With a file-open dialog shown as a sheet (NSOpenPanel via rfd's beginSheetModalForWindow:completionHandler:), dragging a file out of the panel's file list and dropping it outside the app intermittently causes the app's event loop to terminate as if the app had quit: [NSApp run] returns, tao emits LoopDestroyed, and the process exits with code 0. No exit was requested by the application, and the panel's completion handler never fired.
It is a race; it does not reproduce every time, but reliably enough that end users hit it in production.
Steps to reproduce
Tauri v2 app; open a file picker from the frontend (@tauri-apps/plugin-dialogopen({ multiple: true }), which runs FileDialogBuilder::blocking_pick_files → rfd AsyncFileDialog → sheet on the main window).
While the panel is open, drag a file from the panel's file list and drop it outside the app (e.g. on the Desktop).
Intermittently: the app exits.
Evidence the exit is spurious
From a crash report captured at the moment of exit (our app aborts later in exit-time static destructors, which is unrelated to tao; the tao bug is the exit itself):
Main thread is in exit() after main returned; the loop ended and tao ran process::exit with code 0 (control flow was never ExitWithCode).
The dialog was still pending: one thread parked in FileDialogBuilder::blocking_pick_files → std::sync::mpsc::Receiver::recv (the plugin's rendezvous channel; completion handler never ran), and the rfd ModalFuture thread still blocked on the future.
The app's RunEvent::ExitRequested was never observed; no app.exit() / request_exit call exists on this path.
There is no check that tao itself requested the stop (AppState::cleared → msg_send![&app, stop: nil]). Per Apple's docs, -[NSApplication stop:] merely sets a flag that is consumed by whichever run-loop invocation next finishes dispatching an actual NSEvent; a modal panel plus a drag session run nested event loops (the modern NSOpenPanel is additionally hosted out-of-process via ViewBridge). When the drag/panel teardown interleaves badly, a stop intended for (or generated inside) the nested machinery is consumed by the outer [NSApp run], which returns; tao then tears everything down.
This failure class has history in winit that tao no longer carries:
winit #1591 ("dialogs immediately close with run_return") → winit PR #1581 added explicit dialog awareness: it refused to call stop: while a dialog window was open or closing (dialog_is_closing + INTERRUPT_EVENT_LOOP_EXIT).
winit PR #2292 removed that guard as superseded by the observer in-callback guard; tao ported the removal in Fix native file dialogs freezing the event loop, closes #437 #440 (tao 0.12.0). The observer guard prevents a different problem (re-entrant callbacks / freezes, winit #2027) but does nothing about a stray stop reaching the outer loop.
Track whether tao requested the stop (set a flag in the should_exit path immediately before msg_send![&app, stop: nil]). If [NSApp run] returns without that flag set, re-enter run instead of tearing down; alternatively, restore dialog awareness along the lines of winit PR #1581. Happy to test a patch against our reproduction.
macOS: dragging a file out of an open NSOpenPanel intermittently terminates the event loop; any
[NSApp run]return is treated as exitEnvironment
Summary
With a file-open dialog shown as a sheet (
NSOpenPanelvia rfd'sbeginSheetModalForWindow:completionHandler:), dragging a file out of the panel's file list and dropping it outside the app intermittently causes the app's event loop to terminate as if the app had quit:[NSApp run]returns, tao emitsLoopDestroyed, and the process exits with code 0. No exit was requested by the application, and the panel's completion handler never fired.It is a race; it does not reproduce every time, but reliably enough that end users hit it in production.
Steps to reproduce
@tauri-apps/plugin-dialogopen({ multiple: true }), which runsFileDialogBuilder::blocking_pick_files→ rfdAsyncFileDialog→ sheet on the main window).Evidence the exit is spurious
From a crash report captured at the moment of exit (our app aborts later in exit-time static destructors, which is unrelated to tao; the tao bug is the exit itself):
exit()aftermainreturned; the loop ended and tao ranprocess::exitwith code 0 (control flow was neverExitWithCode).FileDialogBuilder::blocking_pick_files→std::sync::mpsc::Receiver::recv(the plugin's rendezvous channel; completion handler never ran), and the rfdModalFuturethread still blocked on the future.RunEvent::ExitRequestedwas never observed; noapp.exit()/request_exitcall exists on this path.Trimmed thread states:
Analysis
src/platform_impl/macos/event_loop.rs(run_return) treats any return of[NSApp run]as an intentional loop end:There is no check that tao itself requested the stop (
AppState::cleared→msg_send![&app, stop: nil]). Per Apple's docs,-[NSApplication stop:]merely sets a flag that is consumed by whichever run-loop invocation next finishes dispatching an actualNSEvent; a modal panel plus a drag session run nested event loops (the modernNSOpenPanelis additionally hosted out-of-process via ViewBridge). When the drag/panel teardown interleaves badly, a stop intended for (or generated inside) the nested machinery is consumed by the outer[NSApp run], which returns; tao then tears everything down.This failure class has history in winit that tao no longer carries:
stop:while a dialog window was open or closing (dialog_is_closing+INTERRUPT_EVENT_LOOP_EXIT).runModal-vs-[NSApp run]interplay: winit #2752 / rfd feat: addminimizableandmaximizableoptions #116.Suggested direction
Track whether tao requested the stop (set a flag in the
should_exitpath immediately beforemsg_send![&app, stop: nil]). If[NSApp run]returns without that flag set, re-enterruninstead of tearing down; alternatively, restore dialog awareness along the lines of winit PR #1581. Happy to test a patch against our reproduction.