Summary
On a desktop whose display is blanked while locked, Hyprland emits repeated
removal events for a phantom monitor named FALLBACK. Quickshell logs
Got removal for monitor FALLBACK which was not previously tracked for every
event, yet keeps processing them and leaks RSS — measured 8.4 GB after 23
hours of locked/idle time. A laptop with no external display does not exhibit
this (no FALLBACK events).
This is the upstream issue a shell maintainer asked us to file after closing
our report; the leak is in Quickshell's monitor-removal handling, not in any
shell config.
Environment
| item |
value |
| OS |
Arch Linux / Omarchy |
| Quickshell |
0.3.1-1 (Arch package) |
| Hyprland |
0.56.2-3 (commit efb50993780079460b0cbed1363e2166a2de1d9f) |
| Qt |
qt6-base 6.11.2-3 |
| Display |
HDMI-A-1 3840x2160 @ scale 1.25 |
| Shell |
a Quickshell-based bar/lock (plain stock behavior reproduces) |
Reproduction
- Let the system idle. Our idle chain: screensaver at 150s, lock at 300s.
- The lock screen blanks the display by disabling DPMS (via
hyprctl dpms off / lock plugin). Hyprland reports There are no outputs.
- WAIT — the session stays locked over several hours. Hyprland keeps emitting
FALLBACK output removal events roughly every 10–12 seconds.
- Watch Quickshell's RSS. It grows monotonically while locked; never reclaimed
after repeated unlock/lock cycles.
Log excerpt (what keeps repeating)
[hh:mm:ss] Got removal for monitor FALLBACK which was not previously tracked
Measured evidence
- RSS growth: 8.4 GB after ~23 h in a locked-but-running session
(unlock does not release it; the process must be restarted).
- Worst observed: 18.4 GB RSS in a single untamed session (27 GB RAM box)
before a local watchdog was added to auto-restart the shell at a cap.
- Event cadence: one
FALLBACK removal every ~10–12 s while outputs are off.
- Laptop check: HDMI absent / no external output loss → no
FALLBACK
events → no growth. The leak is specific to the removal path.
Analysis
- The message
Got removal for monitor FALLBACK which was not previously tracked shows Quickshell detects the unknown monitor and still runs its
removal machinery. Whatever the removal path allocates (screen objects,
layershell surface state, window screens, watchers) is never freed when the
monitor was not tracked.
- A normal monitor hotplug/unplug does not normally remove an untracked
monitor, so this path is rarely exercised — which is why the leak is only
visible with a display that drops to no-outputs while the compositor keeps
pumping FALLBACK events.
- Reproducer-independent: any shell on top of Quickshell shows it; it is not a
particular bar/lock plugin's bug.
Related (separate) upstream problem
Hyprland sending phantom FALLBACK removals while DPMS is intentionally off
may warrant a compositor-side question (hyprland/hyprland), but the shell
crash/leak is fully an upstream Quickshell bug — the FALLBACK removal is
legal input, and handling it must be safe regardless of whether Hyprland's
emission is ideal. A hard fix in Quickshell (never allocate / always free on
the untracked-removal path) would make the leak harmless either way.
Update — same root cause also crashes the shell (2026-09-14)
The output-loss/FALLBACK churn does not only leak RSS; it can also kill the
whole Quickshell process when a shell component runs a deferred teardown
against an object destroyed during monitor removal, turning a benign warning
into a fatal one.
Observed chain (repeated 2x in one boot, identical signature; several more
restarts in the same window):
WARN: attempted to use dangling screen object
WARN: attempted to use dangling screen object
DEBUG: Not creating lock surface for screen QScreen(...) as it is not backed by a valid wayland output.
WARN scene: .../backup-history/Panel.qml[106:-1]: TypeError:
Property 'destroy' of object TypeError: Type error is not a function
(exception occurred during delayed function evaluation)
-> Quickshell aborts -> the whole session ends ("error in client communication")
-> next start shows Hyprland's safe-mode "previous session crashed" banner.
Panel.qml:106 did Qt.callLater(function() { collector.destroy() }). The
collector object was already destroyed by the screen teardown, so destroy()
throws. An if (collector) guard is useless on a stale reference — it is still
truthy. The uncaught exception in the delayed function evaluation is fatal:
Quickshell does not survive it.
Two independent failure modes from the same handling gap:
- Leak — untracked-removal path allocates and never frees.
- Abort — delayed callbacks (
Qt.callLater) referencing screen-owned
objects run after those objects are destroyed, and the exception escapes.
Both point at the same requirement: removal/takedown of an output must
invalidate references held in deferred/JS-land callbacks and make teardown
failure non-fatal.
Ask
- Confirm whether
FALLBACK removal should be a no-op (not an allocation
path) when the monitor was not tracked.
- Consider making exceptions inside
Qt.callLater deferred function
evaluations non-fatal (log + continue), so a stale teardown reference cannot
kill the whole shell.
- I can help test a patch on the reproducing box (23 h locked soak) and report
RSS before/after.
Contact: happy to run any debug build/flags and share full logs.
Summary
On a desktop whose display is blanked while locked, Hyprland emits repeated
removal events for a phantom monitor named
FALLBACK. Quickshell logsGot removal for monitor FALLBACK which was not previously trackedfor everyevent, yet keeps processing them and leaks RSS — measured 8.4 GB after 23
hours of locked/idle time. A laptop with no external display does not exhibit
this (no
FALLBACKevents).This is the upstream issue a shell maintainer asked us to file after closing
our report; the leak is in Quickshell's monitor-removal handling, not in any
shell config.
Environment
Reproduction
hyprctl dpms off/ lock plugin). Hyprland reportsThere are no outputs.FALLBACKoutput removal events roughly every 10–12 seconds.after repeated unlock/lock cycles.
Log excerpt (what keeps repeating)
Measured evidence
(unlock does not release it; the process must be restarted).
before a local watchdog was added to auto-restart the shell at a cap.
FALLBACKremoval every ~10–12 s while outputs are off.FALLBACKevents → no growth. The leak is specific to the removal path.
Analysis
Got removal for monitor FALLBACK which was not previously trackedshows Quickshell detects the unknown monitor and still runs itsremoval machinery. Whatever the removal path allocates (screen objects,
layershell surface state, window screens, watchers) is never freed when the
monitor was not tracked.
monitor, so this path is rarely exercised — which is why the leak is only
visible with a display that drops to no-outputs while the compositor keeps
pumping
FALLBACKevents.particular bar/lock plugin's bug.
Related (separate) upstream problem
Hyprland sending phantom
FALLBACKremovals while DPMS is intentionally offmay warrant a compositor-side question (
hyprland/hyprland), but the shellcrash/leak is fully an upstream Quickshell bug — the
FALLBACKremoval islegal input, and handling it must be safe regardless of whether Hyprland's
emission is ideal. A hard fix in Quickshell (never allocate / always free on
the untracked-removal path) would make the leak harmless either way.
Update — same root cause also crashes the shell (2026-09-14)
The output-loss/
FALLBACKchurn does not only leak RSS; it can also kill thewhole Quickshell process when a shell component runs a deferred teardown
against an object destroyed during monitor removal, turning a benign warning
into a fatal one.
Observed chain (repeated 2x in one boot, identical signature; several more
restarts in the same window):
Panel.qml:106didQt.callLater(function() { collector.destroy() }). Thecollector object was already destroyed by the screen teardown, so
destroy()throws. An
if (collector)guard is useless on a stale reference — it is stilltruthy. The uncaught exception in the delayed function evaluation is fatal:
Quickshell does not survive it.
Two independent failure modes from the same handling gap:
Qt.callLater) referencing screen-ownedobjects run after those objects are destroyed, and the exception escapes.
Both point at the same requirement: removal/takedown of an output must
invalidate references held in deferred/JS-land callbacks and make teardown
failure non-fatal.
Ask
FALLBACKremoval should be a no-op (not an allocationpath) when the monitor was not tracked.
Qt.callLaterdeferred functionevaluations non-fatal (log + continue), so a stale teardown reference cannot
kill the whole shell.
RSS before/after.
Contact: happy to run any debug build/flags and share full logs.