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
{{ message }}
Repository navigation
[Bug]: Desktop app hangs on quit (⌘Q) — deadlock between tauri_plugin_http shutdown and Webview::url() IPC #7548
Quitting the desktop app via ⌘Q (or Menu → Quit) frequently hangs the app completely — the window becomes unresponsive and the process must be force-killed. This has been observed multiple times, with .hang diagnostic reports confirming the process is unresponsive shortly after launch/quit.
I captured a live sample (spindump) of the hung process. It shows a clear deadlock:
Main thread — blocked handling the Cmd+Q key equivalent, inside the applicationWillTerminate → tao/tauri shutdown path:
Six tokio-rt-worker threads — simultaneously blocked waiting for the main thread to service a webview IPC call:
tauri::ipc::InvokeResolver<R>::respond_async_serialized_inner::{{closure}}
tauri::webview::Webview<R>::url
<WryWebviewDispatcher<T> as WebviewDispatch<T>>::url
std::sync::mpmc::Receiver<T>::recv <-- waiting on main thread response
This is a classic mutual-wait deadlock: the main thread is stuck waiting on a tauri_plugin_http shutdown channel inside the applicationWillTerminate handler, while multiple worker threads are stuck waiting for the main thread to respond to Webview::url() calls. Since the main thread never returns to the event loop to service those IPC requests, and tauri_plugin_http's shutdown handler never gets whatever signal it's waiting for, both sides wait forever.
There is also a thread blocked in shared_child::SharedChild::wait() (waitid), presumably waiting for the bundled backend child process to exit — which also can't complete while the above deadlock holds.
Steps to reproduce
Launch Stirling-PDF.app
Quit via ⌘Q (observed most reliably when quitting shortly — within ~30–60s — after launch, though not confirmed as a strict requirement)
App becomes unresponsive; window does not close; process must be force-quit
Expected behavior
App quits cleanly without hanging.
Additional context
Happy to provide the full spindump/sample output or additional .hang diagnostic reports if useful.
I can reproduce this reliably on the current release, and I think I can explain why it doesn't show up every time.
Environment: Stirling-PDF Desktop 3.1.0 and the official 3.2.0 build, macOS 27.0.1, MacBookPro18,3 (M1 Pro). Not signed in. It also happens signed in, so the account doesn't matter.
Repro: launch the app, then close it (red close button) within about 0–2 s of the window appearing. Closing at a calm moment usually works, which may be why it didn't reproduce for you.
Build
Tries
Hung (>30 s, force-killed)
Quit in ~1 s
3.1.0
7
4
3
3.2.0
4
2
2
What the hung process looks like (sample, 3.1.0): the same deadlock as the original report. The main thread is in tauri_plugin_http's exit handler, waiting in Receiver::recv for the cookie-store save. All 8 tokio-rt-worker threads are in Webview::url() waiting for the main thread.
Every hang was preceded by Stirling's own 10 s backend-shutdown timeout (the "did not stop in time" line in tauri-backend.log), and none of the clean quits were. I think that's the trigger:
RunEvent::ExitRequested calls cleanup_backend() on the main thread, which sleep-polls BACKEND_EXITED for up to 10 s.
BACKEND_EXITED is set by the monitor_backend_output task on a tokio worker. If the frontend has fetches in flight, the workers are blocked in Webview::url() waiting for the main thread, which is asleep, so the backend's exit goes unnoticed and the 10 s timeout runs out. That's the freeze people see even when it eventually quits.
The app then reaches RunEvent::Exit, where tauri-plugin-http (cookies feature) hands the cookie save to a tokio task and blocks the main thread on recv(). With every worker still stuck in url(), that never completes.
Library side: step 3 is a tauri-plugin-http bug. tauri-apps/plugins-workspace#3703 (open) replaces that exit-time request_save() + recv() with a direct synchronous save, which would remove the permanent hang once it ships.
Stirling side: steps 1–2 are fixable here regardless: run cleanup_backend() off the main thread (prevent_exit(), do the cleanup on a spawned thread, then app_handle.exit(0)), so the main thread keeps answering the webview while the backend stops. I think this also avoids the permanent hang in practice, because the workers drain before the exit handler runs, but I haven't proven that yet: a build from current main didn't hang in 10 tries for me, so I can't show a before/after until I find what differs from the release builds.
Happy to open a small PR for the Stirling side if you'd like to assign this to me. I can attach the full sample output too.
Correction and follow-up to my comment above: my earlier "a build from main didn't hang" was a flaw in my test harness, not a real difference. With that fixed, current main reproduces it too, so I was able to test both halves. Same machine; each try launches the app and quits within 0–2 s of the window appearing.
Close button
Build
Tries
Hung
~10 s freeze, then quit
Quit in ~1 s
main
17
6
6
5
main + Stirling change (cleanup off the main thread)
12
0
0
12
⌘Q, within the first second
Build
Tries
Hung
main
2
2
main + Stirling change
3
3
main + Stirling change + patched tauri-plugin-http
8
0
(⌘Q one or two seconds after launch quit cleanly on every build, 8 of 8.)
So I need to walk back one guess from above: the Stirling change fixes the close-button path completely in my tests, but not ⌘Q. ⌘Q goes NSApplication terminate: → applicationWillTerminate → RunEvent::Exit without an ExitRequested, so Stirling's handler never runs and the app goes straight into the plugin's cookie-save recv() (confirmed with sample on the Stirling-change build). That half needs the plugin fix: the patch I tested replaces the exit-time request_save() + recv() with a direct synchronous write, the same thing tauri-apps/plugins-workspace#3703 does.
Proposed PR for this repo: just the ExitRequested change in src-tauri/src/lib.rs (around 20 lines). It removes the 10 s freeze and the close-button hang now; ⌘Q will follow once the plugin fix ships and the dependency is bumped. Happy to open it if you assign this to me.
Activity
balazs-szucs commentedon Aug 23, 2026
Hi,
thanks for reporting, i would wait until somebody else can reproduce this. I am afraid i cannot, tried 5 times straight no dice.
Thanks!
github-actions commentedon Sep 22, 2026
github-actions commentedon Sep 29, 2026
amquck commentedon Oct 3, 2026
Experiencing the exact same issue and replicated via the same process as the OP.
mtichikawa commentedon Oct 18, 2026
I can reproduce this reliably on the current release, and I think I can explain why it doesn't show up every time.
Environment: Stirling-PDF Desktop 3.1.0 and the official 3.2.0 build, macOS 27.0.1, MacBookPro18,3 (M1 Pro). Not signed in. It also happens signed in, so the account doesn't matter.
Repro: launch the app, then close it (red close button) within about 0–2 s of the window appearing. Closing at a calm moment usually works, which may be why it didn't reproduce for you.
What the hung process looks like (
sample, 3.1.0): the same deadlock as the original report. The main thread is intauri_plugin_http's exit handler, waiting inReceiver::recvfor the cookie-store save. All 8tokio-rt-workerthreads are inWebview::url()waiting for the main thread.Every hang was preceded by Stirling's own 10 s backend-shutdown timeout (the "did not stop in time" line in
tauri-backend.log), and none of the clean quits were. I think that's the trigger:RunEvent::ExitRequestedcallscleanup_backend()on the main thread, which sleep-pollsBACKEND_EXITEDfor up to 10 s.BACKEND_EXITEDis set by themonitor_backend_outputtask on a tokio worker. If the frontend has fetches in flight, the workers are blocked inWebview::url()waiting for the main thread, which is asleep, so the backend's exit goes unnoticed and the 10 s timeout runs out. That's the freeze people see even when it eventually quits.RunEvent::Exit, wheretauri-plugin-http(cookies feature) hands the cookie save to a tokio task and blocks the main thread onrecv(). With every worker still stuck inurl(), that never completes.Library side: step 3 is a
tauri-plugin-httpbug. tauri-apps/plugins-workspace#3703 (open) replaces that exit-timerequest_save()+recv()with a direct synchronous save, which would remove the permanent hang once it ships.Stirling side: steps 1–2 are fixable here regardless: run
cleanup_backend()off the main thread (prevent_exit(), do the cleanup on a spawned thread, thenapp_handle.exit(0)), so the main thread keeps answering the webview while the backend stops. I think this also avoids the permanent hang in practice, because the workers drain before the exit handler runs, but I haven't proven that yet: a build from currentmaindidn't hang in 10 tries for me, so I can't show a before/after until I find what differs from the release builds.Happy to open a small PR for the Stirling side if you'd like to assign this to me. I can attach the full
sampleoutput too.Investigated with help from Claude Code.
mtichikawa commentedon Oct 18, 2026
Correction and follow-up to my comment above: my earlier "a build from
maindidn't hang" was a flaw in my test harness, not a real difference. With that fixed, currentmainreproduces it too, so I was able to test both halves. Same machine; each try launches the app and quits within 0–2 s of the window appearing.Close button
mainmain+ Stirling change (cleanup off the main thread)⌘Q, within the first second
mainmain+ Stirling changemain+ Stirling change + patchedtauri-plugin-http(⌘Q one or two seconds after launch quit cleanly on every build, 8 of 8.)
So I need to walk back one guess from above: the Stirling change fixes the close-button path completely in my tests, but not ⌘Q. ⌘Q goes
NSApplication terminate:→applicationWillTerminate→RunEvent::Exitwithout anExitRequested, so Stirling's handler never runs and the app goes straight into the plugin's cookie-saverecv()(confirmed withsampleon the Stirling-change build). That half needs the plugin fix: the patch I tested replaces the exit-timerequest_save()+recv()with a direct synchronous write, the same thing tauri-apps/plugins-workspace#3703 does.Proposed PR for this repo: just the
ExitRequestedchange insrc-tauri/src/lib.rs(around 20 lines). It removes the 10 s freeze and the close-button hang now; ⌘Q will follow once the plugin fix ships and the dependency is bumped. Happy to open it if you assign this to me.Investigated with help from Claude Code.