Repository navigation
Description
Steps to reproduce
- Run a Flutter Linux desktop app that uses the
window_managerplugin (observed with FluffyChat, Flathub Flatpakim.fluffychat.Fluffychat). - Use the app normally.
- Close the main window.
The app crashes with SIGSEGV very often (not every time) during shutdown.
Expected results
The app exits cleanly.
Actual results
On exit the following is logged, followed by a segfault:
embedder.cc (2603): 'FlutterEngineRemoveView' returned 'kInvalidArguments'. Remove view info was invalid. The implicit view cannot be removed.
The crash happens on the main thread while g_application_run() is still iterating the main loop: a GLib timeout source fires and its callback in libflutter_linux_gtk.so casts its user_data to FlTaskRunner, but that object has already been finalized.
Note that user_data of the timeout source and type_instance in the failed cast are the same address (0x55c78106db20).
fl_task_runner_tasks_did_change_locked() schedules
g_timeout_add(..., fl_task_runner_on_expired_timeout, self) without holding a reference to self. If the FlTaskRunner is disposed/finalized during engine shutdown while this timeout is still pending, the source is not removed and fl_task_runner_on_expired_timeout() runs on freed memory (FL_TASK_RUNNER(data) → g_type_check_instance_cast → SIGSEGV).
Possible fixes: remove timeout_source_id in fl_task_runner_dispose(), or let the timeout source hold a strong or weak reference to the task runner.
This is the same class of bug as #192873 (idle source in FlView firing after the view was destroyed). window_manager seems to make the race much more likely by changing how the view is torn down (see leanflutter/window_manager#585). Possibly also related: #164564.
Code sample
Code sample
no minimal sample yet. Reproduced with FluffyChat (https://github.com/krille-chan/fluffychat), Linux Flatpak build. According to leanflutter/window_manager#585, just adding `window_manager` as a dependency is enough to get the `FlutterEngineRemoveView` error on exit.
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
Logs
Logs
Backtrace (gdb via flatpak-coredumpctl, GNOME 50 SDK + debug symbols)
``` Signal: 11 (SEGV)#0 g_type_check_instance_cast (type_instance=0x55c78106db20, iface_type=0x55c7813e8560 [FlTaskRunner]) at ../gobject/gtype.c:3949
#1 0x00007f6fe94f5a7d in ??? () at /app/fluffychat/lib/libflutter_linux_gtk.so
#2 0x00007f6fe8106901 in g_timeout_dispatch (source=0x55c782de0dc0, callback=, user_data=) at ../glib/gmain.c:5324
#3 0x00007f6fe810252b in g_main_dispatch (context=context@entry=0x55c780ee79e0) at ../glib/gmain.c:3591
dispatch = 0x7f6fe81068e0 <g_timeout_dispatch>
user_data = 0x55c78106db20
callback = 0x7f6fe94f5a30
source = 0x55c782de0dc0
#4 0x00007f6fe8105957 in g_main_context_dispatch_unlocked (context=0x55c780ee79e0) at ../glib/gmain.c:4451
#5 g_main_context_iterate_unlocked (context=context@entry=0x55c780ee79e0, block=block@entry=0, dispatch=dispatch@entry=1, self=) at ../glib/gmain.c:4516
#6 0x00007f6fe81061b3 in g_main_context_iteration (context=context@entry=0x55c780ee79e0, may_block=may_block@entry=0) at ../glib/gmain.c:4582
#7 0x00007f6fe82feaaa in g_application_run (application=0x55c780ee6050 [MyApplication], argc=, argv=) at ../gio/gapplication.c:2773
#8 0x000055c75085d4ae in main ()
All other threads are idle (tokio workers from vodozemac, glycin image loader threads).
</details>
### Flutter Doctor output
<details open><summary>Doctor output</summary>
Not built locally. The app is the official FluffyChat Flatpak `<FluffyChat-Version>` (Flutter version: see FluffyChat release notes), runtime `org.gnome.Platform//50`, GLib 2.88.3, Fedora `<Version>`, `<GNOME/KDE>` on Wayland.
</details>
Activity
suryaKommana2662 commentedon Oct 5, 2026
@Mk-strg Thanks for the detailed report. Could you provide a minimal Flutter Linux application that reproduces the issue? In particular, a minimal example using
window_managerthat demonstrates the crash during shutdown would help isolate the lifecycle issue from FluffyChat.Mk-strg commentedon Oct 5, 2026
Hi thanks for reviewing this issue. I was able to reproduce this with a minimal app that only depends on
window_manager(0.5.2), built natively on the host with Flutter 3.47.6 (current stable). No Flatpak, no FluffyChat.Steps to reproduce
The app keeps the engine busy (continuous
CircularProgressIndicatoranimation plus a 5 ms periodic platform call) and closes itself after a random delay of 1.5–3 s viawindowManager.close(), which emits the samedelete-eventas clicking the close button.repro.shstarts the app repeatedly and counts crashes by exit code.lib/main.dart
repro.sh
Results
3 crashes with SIGSEGV (exit code 139) in 82 runs (~3.7%), 0 SIGABRT.
The following message is printed on every exit (82/82), including the clean ones:
So
window_managersends the engine through an invalid view-removal path on every shutdown, and the crash happens only when a task-runner timeout is still pending at that moment.Backtrace of a crash from the minimal app
All other threads are idle (Mesa util_queue workers, GDBus, dconf, glycin).
This matches the original report: a GLib timeout source created by the embedder fires during shutdown after its owner has been freed. In current stable,
FlTaskRunnerstores the engine in aGWeakRef. In this release build the crash shows up atg_weak_ref_get()on the freed task runner, rather than in theFL_TASK_RUNNER()cast seen with the older Flutter version used by FluffyChat (g_type_check_instance_cast(..., [FlTaskRunner])). Symbols forlibflutter_linux_gtk.soshould confirm that frames #3–#4 arefl_task_runner_on_expired_timeout.In FluffyChat, the crash is most frequent when the window is closed while an animation is running (e.g. the typing indicator), which fits the "pending engine task during teardown" theory.
Environment
Fedora 44, KDE Plasma (kde-gtk-config 6.7.5), Wayland, Mesa 26.2.3, GTK 3.24.52, GLib 2.88.3, window_manager 0.5.2
suryaKommana2662 commentedon Oct 6, 2026
@Mk-strg Thanks for providing the minimal reproduction. I was able to reproduce the crash locally using the provided reproduction setup. On my local Linux environment, running the reproduction script 5 times resulted in:
The issue has sufficient information for further investigation, including a minimal reproduction, crash logs, and a backtrace. Routing this to the
team-linuxfor further investigation.Flutter Doctor Output
Mk-strg commentedon Oct 6, 2026
Thanks for confirming! Interesting that you also hit the SIGABRT variant. I saw that one in FluffyChat as well (libepoxy assertion, "Couldn't find current GLX or EGL context"), so both signatures seem to come from the same shutdown race. Happy to test a fix once there is one.
siegblink commentedon Oct 7, 2026
We hit the same segfault on quit (
g_type_check_instance_cast+48, main thread) in a Linux app that uses window_manager 0.5.2 on Flutter 3.47.2, and looked into whyfl_task_runner_dispose()doesn't remove the pending timeout. It removestimeout_source_id(L123-L136), but the pending source can be one the runner no longer tracks:fl_task_runner_on_expired_timeouthasn't takenself->mutexyet (L75-L83).fl_task_runner_tasks_did_change_lockedcallsg_source_remove(X)(X is already dispatching, so its callback still runs), adds timeout Y and setstimeout_source_id = Y(L108-L120).self->timeout_source_id = 0;. Y is still attached, but nothing tracks it.disposethen removes nothing, or only a later tracked timeout, and Y fires on the freed runner:FL_TASK_RUNNER(data)→g_type_check_instance_cast→ SIGSEGV. When Y fires while the runner is alive, its callback zeroes the id again, so under continuous load the next timeout is orphaned in turn.With the default UI-thread policy Dart runs on the platform thread, so every dart:io, isolate or FFI wake-up posts to the runner from another thread. A standalone C copy of
fl_task_runner.cc's logic on GLib 2.80 shows the race without any delay added: with two or four threads posting every 1.5 to 9 ms, 9 of 12 ten-second runs left untracked timeouts (up to 15 per run), each one from a poster removing the timeout the main thread had started; with one posting thread, none. One run under continuous posting carried an untracked timeout through 3,315 callbacks. A quit crashes only when the runner is freed while one is pending, which fits the crash being intermittent. Under gdb, adding an untracked task-runner timeout at the engine's dispose reproduces the top of the reported backtrace:g_type_check_instance_castcalled fromfl_task_runner_on_expired_timeout, from GLib's timeout dispatch.Possible fixes: in
fl_task_runner_on_expired_timeout, cleartimeout_source_idonly when it is the id of the source being dispatched (g_source_get_id(g_main_current_source())); or let each timeout hold a reference to the runner, and stop rescheduling once the engine is gone.App-side workaround that avoids it for us: the runner keeps a reference to the engine and drops it after
g_application_run()returns, so the engine, its task runner and the plugins are disposed when the main loop can no longer dispatch anything:Mk-strg commentedon Oct 7, 2026
@siegblink Thanks a lot for the detailed root-cause analysis and the workaround, that was really helpful! I can confirm both on Flutter 3.47.6 with the minimal reproducer:
Workaround as applied to the default
linux/runner/my_application.cc(everything else unchanged)struct _MyApplication { GtkApplication parent_instance; char** dart_entrypoint_arguments; + FlEngine* engine; }; // in my_application_activate(): FlView* view = fl_view_new(project); + g_set_object(&self->engine, fl_view_get_engine(view)); // in my_application_dispose(): g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + g_clear_object(&self->engine); G_OBJECT_CLASS(my_application_parent_class)->dispose(object);main.ccis the unmodified template (g_autoptr(MyApplication)), so the app and thus the engine are released afterg_application_run()returns.The
FlutterEngineRemoveView ... kInvalidArgumentsmessage is still printed on every exit (200/200). The workaround doesn't change the teardown path; it only ensures the engine and its task runner outlive the main loop, so an orphaned timeout can no longer fire on a freed runner.That said, this is only a workaround and the underlying race in
FlTaskRunnershould still be fixed in the engine:flutter createtemplate is affected, since the template doesn't keep a reference to the engine. Most app developers won't know they need this.window_manager. It only needs other threads posting tasks while the runner is disposed (dart:io, isolates, FFI);window_managerjust makes the teardown timing worse.Either of the fixes suggested above would address the root cause: only clearing
timeout_source_idinfl_task_runner_on_expired_timeoutwhen it matches the source being dispatched (g_source_get_id(g_main_current_source())), or having each timeout hold a reference to the runner and stop rescheduling once the engine is gone.For completeness, one more crash signature from a run without the workaround. It's the timeout callback touching
self->pending_tasksof the freed runner:Fix use-after-free of FlTaskRunner timeout on shutdown (flutter#194525)
Fix use-after-free of FlTaskRunner timeout on shutdown (flutter#194525)
Fix use-after-free of FlTaskRunner timeout on shutdown (flutter#194525)
github-actions commentedon Nov 4, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.