Please confirm the following.
What version of the Modrinth App are you using?
Modrinth App 0.19.1 (Flathub com.modrinth.ModrinthApp, system install) — Ubuntu 24.04, X11/XFCE, 28 GiB RAM
What operating systems are you seeing the problem on?
Linux
Describe the bug
The app leaks one open file descriptor on app.db (plus ~2 MB of RSS) per instance-watcher event. On a large library this accumulates into a multi-GB launcher process, then collapses into a self-sustaining error storm that kills the WebKit renderer — leaving a grey, unresponsive window whose native titlebar still works, so the user can right-click → Close it.
This is measured, not inferred. I have a clean control window at the end.
1. The fd leak
Live process, 3h48m into a session:
$ ls -l /proc/<pid>/fd | awk '{print $NF}' | sort | uniq -c | sort -rn | head
1673 .../ModrinthApp/app.db
100 .../ModrinthApp/app.db-wal
11 anon_inode:[eventfd]
9 /dev/dri/renderD128
5 anon_inode:[timerfd]
1,774 open descriptors on the SQLite database. The process fd limit is 1048576, so this is not EMFILE — nothing forces a cleanup, it just grows.
RSS at that same moment was 3,683 MiB. That works out to 2.08 MB per leaked descriptor, which is exactly SQLite's default per-connection page cache (cache_size = -2000, i.e. 2000 KiB). So each leaked fd is a whole live connection carrying its own page cache, not a bare descriptor.
Note that 1,774 is far beyond any single sqlx pool's max_connections, which suggests connections (or pools) are being created per-event and dropped without being closed, rather than one pool overflowing.
2. The trigger is the instance file watcher
theseus::state::instances::watcher fires roughly once a second the whole time Minecraft is running:
INFO init_watcher: theseus::state::instances::watcher: World updated: /home/<user>/.var/app/com.modrinth.ModrinthApp/data/ModrinthApp/profiles/<instance>/saves/<world>/level.dat
This library is large — 47 profiles, 7,504 directories, 26 GB — which is presumably why it shows up here and not on a typical install.
3. Control: the leak is per-event, not per-unit-time
Same process, same session, sampled once a minute for 7 minutes while the user was idle (game still running, but no world-save events):
11:55:39 fds=1835 rss=3683MiB
11:56:39 fds=1834 rss=3683MiB
11:57:39 fds=1834 rss=3684MiB
11:58:39 fds=1833 rss=3684MiB
11:59:39 fds=1834 rss=3682MiB
12:00:39 fds=1834 rss=3683MiB
12:01:39 fds=1833 rss=3683MiB
Completely flat — and the launcher log has zero lines of any kind in that window. Compare the active part of the same session:
| Window |
Watcher events |
fd growth |
| 08:06 → 10:15 (playing) |
~1/sec |
+1,774 |
| 11:55 → 12:02 (idle) |
none |
0 |
So it leaks at roughly 830 fds/hour while actively playing and zero when idle. Leaving the app open overnight is harmless; an 8-hour play session is what kills it.
4. Collapse and renderer death
Once it saturates, the database starts failing, in this order:
ERROR theseus::state::instances::watcher: Failed to sync instance content after filesystem change:
Error interacting with database: error returned from database: (code: 5) database is locked
... then: (code: 14) unable to open database file
... then: pool timed out while waiting for an open connection
Every one of these is pushed to the frontend by theseus_gui::error from packages/app-lib/src/api/instance/get.rs:17. Measured rate, counting log lines per second:
4193 2026-08-29T10:31:59
4748 2026-08-29T10:32:00
3919 2026-08-29T10:32:01
3788 2026-08-29T10:32:02
4323 2026-08-29T10:32:03
~4,000–5,000 errors per second. Totals across this machine's logs: 1,390,704 (code: 14) unable to open database file lines, 338k errors inside one 2-minute window, a single 189 MB session log, and 1.1 GB in launcher_logs/ overall.
Feeding that many error events into the WebView drives WebKitWebProcess to 11–15 GiB RSS, at which point it either gets OOM-killed or crashes on its own. Either way the Tauri window frame survives the renderer, which is the grey-but-closable window.
Elapsed time from first database error to dead renderer is consistently about two minutes:
| Session start |
First DB error |
Renderer death |
| Aug 29 08:03 |
10:29:14 |
OOM-killed 10:31:36 — WebKitWebProcess 11,774 MiB |
| Aug 29 15:57 |
Aug 30 06:24:56 |
OOM-killed 06:27:08 — WebKit 13,605 MiB + launcher 12,478 MiB |
| Aug 27 15:53 |
— |
OOM-killed 15:50:13 — WebKitWebProcess 14,715 MiB |
| Aug 25 / 26 / 28 (×2) |
— |
self-crash, coredumpctl shows SIGABRT / SIGTRAP in WebKitWebProcess |
That is roughly once a day for this user.
Steps to reproduce
- Create a library with a large number of instances (47 here; the effect should scale with instance and directory count).
- Launch any instance and play for several hours, so the watcher sees continuous
level.dat writes.
- Watch the launcher process:
watch -n60 'ls /proc/$(pgrep -f modrinth-app-wrapped)/fd | wc -l'
- The count climbs monotonically and never drops. RSS tracks it at ~2 MB per fd.
- Eventually the database errors begin and the window goes grey within ~2 minutes.
Expected behavior
Each watcher-driven database interaction should release its connection when finished, so the open-fd count on app.db stays bounded (at the pool's max_connections) regardless of session length or library size.
Separately — and worth fixing independently, since it is what turns a slow leak into a hard failure — the frontend error path should rate-limit or coalesce identical errors. A failure mode that emits 4,000 identical errors per second into the WebView will take the renderer down no matter what the underlying cause is.
Additional context
Possibly related, though I think both are downstream of different causes: #3414 (long-running memory growth, never root-caused) and #7276 (WebKitWebProcess CPU/memory loop on Linux). This report is specifically about the app.db descriptor leak, which I haven't seen described anywhere.
Happy to pull further diagnostics from this machine — it reproduces daily. Paths above are redacted; otherwise everything is verbatim.
Please confirm the following.
What version of the Modrinth App are you using?
Modrinth App 0.19.1 (Flathub
com.modrinth.ModrinthApp, system install) — Ubuntu 24.04, X11/XFCE, 28 GiB RAMWhat operating systems are you seeing the problem on?
Linux
Describe the bug
The app leaks one open file descriptor on
app.db(plus ~2 MB of RSS) per instance-watcher event. On a large library this accumulates into a multi-GB launcher process, then collapses into a self-sustaining error storm that kills the WebKit renderer — leaving a grey, unresponsive window whose native titlebar still works, so the user can right-click → Close it.This is measured, not inferred. I have a clean control window at the end.
1. The fd leak
Live process, 3h48m into a session:
1,774 open descriptors on the SQLite database. The process fd limit is 1048576, so this is not
EMFILE— nothing forces a cleanup, it just grows.RSS at that same moment was 3,683 MiB. That works out to 2.08 MB per leaked descriptor, which is exactly SQLite's default per-connection page cache (
cache_size = -2000, i.e. 2000 KiB). So each leaked fd is a whole live connection carrying its own page cache, not a bare descriptor.Note that 1,774 is far beyond any single sqlx pool's
max_connections, which suggests connections (or pools) are being created per-event and dropped without being closed, rather than one pool overflowing.2. The trigger is the instance file watcher
theseus::state::instances::watcherfires roughly once a second the whole time Minecraft is running:This library is large — 47 profiles, 7,504 directories, 26 GB — which is presumably why it shows up here and not on a typical install.
3. Control: the leak is per-event, not per-unit-time
Same process, same session, sampled once a minute for 7 minutes while the user was idle (game still running, but no world-save events):
Completely flat — and the launcher log has zero lines of any kind in that window. Compare the active part of the same session:
So it leaks at roughly 830 fds/hour while actively playing and zero when idle. Leaving the app open overnight is harmless; an 8-hour play session is what kills it.
4. Collapse and renderer death
Once it saturates, the database starts failing, in this order:
Every one of these is pushed to the frontend by
theseus_gui::errorfrompackages/app-lib/src/api/instance/get.rs:17. Measured rate, counting log lines per second:~4,000–5,000 errors per second. Totals across this machine's logs: 1,390,704
(code: 14) unable to open database filelines, 338k errors inside one 2-minute window, a single 189 MB session log, and 1.1 GB inlauncher_logs/overall.Feeding that many error events into the WebView drives
WebKitWebProcessto 11–15 GiB RSS, at which point it either gets OOM-killed or crashes on its own. Either way the Tauri window frame survives the renderer, which is the grey-but-closable window.Elapsed time from first database error to dead renderer is consistently about two minutes:
coredumpctlshows SIGABRT / SIGTRAP in WebKitWebProcessThat is roughly once a day for this user.
Steps to reproduce
level.datwrites.watch -n60 'ls /proc/$(pgrep -f modrinth-app-wrapped)/fd | wc -l'Expected behavior
Each watcher-driven database interaction should release its connection when finished, so the open-fd count on
app.dbstays bounded (at the pool'smax_connections) regardless of session length or library size.Separately — and worth fixing independently, since it is what turns a slow leak into a hard failure — the frontend error path should rate-limit or coalesce identical errors. A failure mode that emits 4,000 identical errors per second into the WebView will take the renderer down no matter what the underlying cause is.
Additional context
Possibly related, though I think both are downstream of different causes: #3414 (long-running memory growth, never root-caused) and #7276 (WebKitWebProcess CPU/memory loop on Linux). This report is specifically about the
app.dbdescriptor leak, which I haven't seen described anywhere.Happy to pull further diagnostics from this machine — it reproduces daily. Paths above are redacted; otherwise everything is verbatim.