Skip to content

omarchy-menu-keybindings Lua stub makes ipairs infinite (Super+K hang with plugins using hl.get_windows) #15216

Open

Description

@AlxWolfenstein97

Summary

omarchy-menu-keybindings (SUPER + K) builds a Lua bind cache by dofileing ~/.config/hypr/hyprland.lua against a stub hl. That stub’s self-referential noop makes ipairs(hl.get_windows()) never terminate, so any Hyprland toggle/plugin that iterates windows at load time wedges the cheatsheet at ~100% CPU until kill/restart.

Reproduced with duncio/omarchy-cava (backdrop visualizer that recovers its window via ipairs(hl.get_windows()) on load). Same class of bug will hit any future toggle that does the same.

Environment

  • Omarchy 4.0.4-1
  • Hyprland 0.56.2
  • omarchy-menu-keybindings from packaged /usr/share/omarchy/bin/omarchy-menu-keybindings

Steps to reproduce

  1. Install a Hyprland toggle that runs ipairs(hl.get_windows()) at load (e.g. enable io.github.duncio.omarchy-cava, which installs ~/.local/state/omarchy/toggles/hypr/omarchy-cava.lua).
  2. Run omarchy-menu-keybindings --print or press SUPER + K.

Expected

Bind list prints / cheatsheet opens quickly.

Actual

lua child spins at ~100% CPU forever; cheatsheet never appears. Process tree looks like:

omarchy-menu-keybindings → lua (R, ~100% CPU)

Root cause

In build_lua_bind_cache, the stub is effectively:

noop = setmetatable({}, {
  __index = function() return noop end,
  __call = function() return noop end,
})
-- hl.get_windows → noop via __index; noop() → noop via __call

Lua 5.4 ipairs uses __index and stops only on nil. So ipairs(noop) yields forever.

The hang is during require("default.hypr.toggles") after bind lines have already been printed.

The same pattern exists in third-party scanners (e.g. keymap plugin scripts/scan.lua).

Suggested fix (Omarchy)

Make the scanner noop safe for ipairs / #:

noop = setmetatable({}, {
  __index = function(_, key)
    if type(key) == "number" then return nil end
    return noop
  end,
  __call = function() return noop end,
  __len = function() return 0 end,
})

Optionally also set an env like OMARCHY_KEYMAP_SCAN=1 (keymap already honors this) so toggles can skip live bookkeeping intentionally.

Related

Plugin-side report: will link to duncio/omarchy-cava issue after create.

Diagnostic log locally: /tmp/omarchy-debug.log (from omarchy-debug --no-sudo --print).

Activity

  1. AlxWolfenstein97 commented on Oct 12, 2026

    @AlxWolfenstein97
    Author

    Plugin-side report (omarchy-cava infinite ipairs(hl.get_windows()) under the stub): duncio/omarchy-cava#1

  2. aholbreich commented on Oct 12, 2026

    @aholbreich

    This is #7025, the same ipairs hang through the scan's self-returning stub. #12124, #13979, #14243 and #14302 report it too (#12052 is already closed). The fix is #7564, which gives the stub real empty lists and time-boxes the scan.

    I reproduced your case with the real omarchy-menu-keybindings --print in a sandbox (stubbed hyprctl, fake HOME seeded from the tree's config/hypr), adding a toggle ~/.local/state/omarchy/toggles/hypr/cava.lua that runs for _, w in ipairs(hl.get_windows()) do end:

    Tree Result
    v4.0.4 hangs until killed at 25 s, no binds listed
    quattro (4c1b4cb) returns, but only because the scan already aborts earlier at default/hypr/qconsole.lua, so every user bind is listed without its dispatcher (also fixed by #7564)
    #7564 rebased onto quattro returns in ~50 ms, user binds dispatchable

    So on the current release the hang is exactly as you describe, and on quattro it's hidden behind the other scan bug. Your plugin-side report (duncio/omarchy-cava#1) is still worth keeping; any toggle that walks windows at load hits this until #7564 lands.

  3. c4software commented on Oct 19, 2026

    @c4software
    Contributor

    @dhh friendly ping: this hang (and its duplicates #7025, #12124, #13979, #14243, #14302) is still present in the packaged 4.0.5-1, and a fix is already waiting in several open PRs. They all make the scan's noop stub return nil for a numeric index so ipairs() over a stubbed getter ends:

    Any of them would close this. More context from a real config in #7564 (comment).

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