Skip to content

Untracked FALLBACK monitor removal leaks RSS and can abort the shell #1139

Description

@MrOshimaida

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

  1. Let the system idle. Our idle chain: screensaver at 150s, lock at 300s.
  2. The lock screen blanks the display by disabling DPMS (via
    hyprctl dpms off / lock plugin). Hyprland reports There are no outputs.
  3. WAIT — the session stays locked over several hours. Hyprland keeps emitting
    FALLBACK output removal events roughly every 10–12 seconds.
  4. 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:

  1. Leak — untracked-removal path allocates and never frees.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions