Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-1816GitHub issuePublicFixed

omarchy-menu-keybindings hangs forever when a user Lua config iterates hl.get_monitors()

Reported by e-defix · filed Aug 15, 2026 · open at source

Validation verdict: Reproduced. An engine-executed run exited 1 and showed the defect signal. Awaiting a human decision.

At a glance

1 · What is claimed?
  • ConfirmedViolates SW-REQ-260922-0W96: With user config that iterates hl.get_monitors() via ipairs, the keybindings menu shows no binds at all: the lua bind scan never completes, so the source-derived cache is never built and the menu never opens - the binds are never shown nor dispatchable.
  • ConfirmedViolates SYS-REQ-260922-6642: omarchy-menu-keybindings --print hangs indefinitely: it performs neither its documented side effect (print the binding list) nor a loud, exit-coded refusal.
  • ConfirmedReproducible: A user Hyprland Lua config that iterates hl.get_monitors() with ipairs at load time hangs omarchy-menu-keybindings --print indefinitely; only an external timeout ends it.
  • ConfirmedAffects the menu component
2 · What does it affect?
AreaIn the audited areamenu· Decided by agents · verified
3 · Is it reproducible?
An engine-executed run exited 1 and showed the defect signal. See reproducer
4 · What remains unclear?
Nothing recorded as open.
5 · What happens next?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: in the audited area (menu) — decided by agents · verified; matches no known issue — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.

Original report

omarchy-menu-keybindings hangs forever when a user Lua config iterates hl.get_monitors()revision 1 · captured 2026-09-28 21:29 UTC via poll

What happened

omarchy-menu-keybindings (SUPER + K) never opens. The process hangs indefinitely — no menu, no error, no timeout. omarchy-menu-keybindings --print also hangs.

The hang is triggered by any user Lua config that iterates one of Hyprland's list getters (hl.get_monitors(), hl.get_windows(), hl.get_workspaces()) with ipairs at config-evaluation time.

What I expected

The keybindings menu opens and lists the bindings, or fails loudly.

Steps to reproduce

On Omarchy 4.0.0 (quattro), add a single line to ~/.config/hypr/monitors.lua:

for _, monitor in ipairs(hl.get_monitors()) do end

Then run:

omarchy-menu-keybindings --print   # hangs forever

Hyprland itself is fine — hyprctl reload and hyprctl configerrors are clean, and the config behaves correctly at runtime. Only the keybindings scanner hangs.

Root cause

build_lua_bind_cache() in omarchy-menu-keybindings re-evaluates ~/.config/hypr/hyprland.lua in a bare lua interpreter, with hl replaced by a catch-all stub (bin/omarchy-menu-keybindings, around lines 181-232):

local noop
noop = setmetatable({}, {
  __index = function() return noop end,
  __call  = function() return noop end,
})

Because __index answers every key with a truthy value, hl.get_monitors() returns noop, and ipairs(noop) reads index 1, 2, 3, … without ever hitting nil. Infinite loop inside the lua subprocess. The while read loop feeding off it in bash blocks forever, so the menu never renders.

The stub already special-cases one getter to return a real value:

get_config = function()
  return nil
end,

The list getters just were not covered.

Suggested fix

Stub the list getters with real empty tables, so iteration terminates:

get_monitors   = function() return {} end,
get_windows    = function() return {} end,
get_workspaces = function() return {} end,

A timeout around the lua subprocess would also turn this class of failure into a degraded menu instead of a silent hang.

Why this probably was not caught

No config under /usr/share/omarchy/default/hypr/ calls any hl.get_* getter at load time (grep -rn "get_monitors\|get_windows\|get_workspaces" /usr/share/omarchy/default/hypr/ returns nothing), so the stock configs never hit it.

Third-party Lua modules do. Unmodified upstream shezdy/hyprsplit init.lua iterates all three getters, so any user requiring it from their Hypr config gets the same hang.

My own trigger was dynamic monitor placement — computing the laptop's position from the live external monitor geometry, rather than hardcoding auto-right.

Workaround

Guard in ~/.config/hypr/hyprland.lua, right after the bootstrap dofile, detecting the stub (raw slot empty but __index conjures a value) and handing back real empty lists:

local probe = hl.get_monitors()
if type(probe) == "table" and rawget(probe, 1) == nil and probe[1] ~= nil then
  hl.get_monitors = function() return {} end
  hl.get_windows = function() return {} end
  hl.get_workspaces = function() return {} end
end

With that in place omarchy-menu-keybindings --print exits 0 and lists all 231 bindings.

System details

  • Omarchy 4.0.0-1 (stable channel)
  • Hyprland 0.56.2-1
  • Lua 5.5.1-1
  • CPU: AMD Ryzen AI 7 PRO 350 w/ Radeon 860M
  • GPU: AMD Radeon 840M / 860M Graphics (Krackan)
View raw text
### What happened

`omarchy-menu-keybindings` (SUPER + K) never opens. The process hangs indefinitely — no menu, no error, no timeout. `omarchy-menu-keybindings --print` also hangs.

The hang is triggered by any user Lua config that iterates one of Hyprland's list getters (`hl.get_monitors()`, `hl.get_windows()`, `hl.get_workspaces()`) with `ipairs` at config-evaluation time.

### What I expected

The keybindings menu opens and lists the bindings, or fails loudly.

### Steps to reproduce

On Omarchy 4.0.0 (quattro), add a single line to `~/.config/hypr/monitors.lua`:

```lua
for _, monitor in ipairs(hl.get_monitors()) do end
```

Then run:

```bash
omarchy-menu-keybindings --print   # hangs forever
```

Hyprland itself is fine — `hyprctl reload` and `hyprctl configerrors` are clean, and the config behaves correctly at runtime. Only the keybindings scanner hangs.

### Root cause

`build_lua_bind_cache()` in `omarchy-menu-keybindings` re-evaluates `~/.config/hypr/hyprland.lua` in a bare `lua` interpreter, with `hl` replaced by a catch-all stub (bin/omarchy-menu-keybindings, around lines 181-232):

```lua
local noop
noop = setmetatable({}, {
  __index = function() return noop end,
  __call  = function() return noop end,
})
```

Because `__index` answers **every** key with a truthy value, `hl.get_monitors()` returns `noop`, and `ipairs(noop)` reads index 1, 2, 3, … without ever hitting `nil`. Infinite loop inside the `lua` subprocess. The `while read` loop feeding off it in bash blocks forever, so the menu never renders.

The stub already special-cases one getter to return a real value:

```lua
get_config = function()
  return nil
end,
```

The list getters just were not covered.

### Suggested fix

Stub the list getters with real empty tables, so iteration terminates:

```lua
get_monitors   = function() return {} end,
get_windows    = function() return {} end,
get_workspaces = function() return {} end,
```

A timeout around the `lua` subprocess would also turn this class of failure into a degraded menu instead of a silent hang.

### Why this probably was not caught

No config under `/usr/share/omarchy/default/hypr/` calls any `hl.get_*` getter at load time (`grep -rn "get_monitors\|get_windows\|get_workspaces" /usr/share/omarchy/default/hypr/` returns nothing), so the stock configs never hit it.

Third-party Lua modules do. Unmodified upstream [shezdy/hyprsplit](https://github.com/shezdy/hyprsplit) `init.lua` iterates all three getters, so any user requiring it from their Hypr config gets the same hang.

My own trigger was dynamic monitor placement — computing the laptop's position from the live external monitor geometry, rather than hardcoding `auto-right`.

### Workaround

Guard in `~/.config/hypr/hyprland.lua`, right after the bootstrap `dofile`, detecting the stub (raw slot empty but `__index` conjures a value) and handing back real empty lists:

```lua
local probe = hl.get_monitors()
if type(probe) == "table" and rawget(probe, 1) == nil and probe[1] ~= nil then
  hl.get_monitors = function() return {} end
  hl.get_windows = function() return {} end
  hl.get_workspaces = function() return {} end
end
```

With that in place `omarchy-menu-keybindings --print` exits 0 and lists all 231 bindings.

### System details

- Omarchy 4.0.0-1 (stable channel)
- Hyprland 0.56.2-1
- Lua 5.5.1-1
- CPU: AMD Ryzen AI 7 PRO 350 w/ Radeon 860M
- GPU: AMD Radeon 840M / 860M Graphics (Krackan)

Also reported as

R-1816 is the earliest report of this defect, filed Aug 15, 2026. 4 later reports describe the same defect.

  1. Aug 15, 2026R-1816 · this report— the original
  2. ·Decided by agents · verified

    Closed upstream on 2026-10-12 by the bot omarchybot, as a duplicate (target not recorded).

  3. ·Decided by agents · verified

    Open upstream.

  4. ·Decided by agents · verified

    Open upstream.

  5. ·Decided by agents · verified

    Open upstream.

Validation

AI validator opencode-promoter-1 (xiaomi-token-plan-sgp/mimo-v2.6-pro) · evaluated revision 1 at product commit ecae57a237ef · sealed 2026-10-10T05:03:36Z

Validator's summary (commentary, not evidence)

The report says omarchy-menu-keybindings and --print hang forever when the user Hyprland Lua config iterates an hl list getter (hl.get_monitors(), hl.get_windows(), hl.get_workspaces()) with ipairs at config-evaluation time, while Hyprland itself stays fine. build_lua_bind_cache in bin/omarchy-menu-keybindings re-evaluates ~/.config/hypr/hyprland.lua under a stub hl whose catch-all __index answers every key with a truthy noop sentinel, so ipairs over a list getter never reaches nil and the lua subprocess spins forever; the bash while-read loop feeding off it blocks, and the menu neither opens nor refuses. The reproducer drives the real bin/omarchy-menu-keybindings --print with a stock user config tree plus the reported one line of user Lua, placed ahead of require("default.hypr.omarchy") because at this baseline that require reaches default/hypr/qconsole.lua, whose numeric comparison throws against the same stub and makes the pcall abort the scan before later user files load (the masking the issue thread documents) - the defect is the stub's non-terminating ipairs, reached by any user Lua the scan evaluates. The engine run is killed by the reproducer's own watchdog after 8 seconds and prints the defect-present marker; with the list getters stubbed to real empty tables the same run terminates and lists the bindings. KI-MENU-KEYBINDINGS-SCAN-DOFILE-ABORT is the same stub aborting the scan early on a throwing comparison (later binds silently vanish) rather than never returning; KI-MENU-KEYBINDINGS-NO-TIMEOUT is unbounded waits in hyprctl binds / xkbcli compile-keymap, not the lua subprocess walking the hl stub.

  • Violates SW-REQ-260922-0W96: With user config that iterates hl.get_monitors() via ipairs, the keybindings menu shows no binds at all: the lua bind scan never completes, so the source-derived cache is never built and the menu never opens - the binds are never shown nor dispatchable.

    Engine: engine-executed run exited 1 and showed the defect signal

    What “confirmed” means here: the requirement exists and the engine reproduced the defect. That the reproducer exercises THIS requirement is the validator's argument (see rationale).

    Evidence: requirement SW-REQ-260922-0W96 · file pocs/reproducers/a withheld known issue/repro.sh

    Validator's rationale (commentary): SW-REQ-260922-0W96: 'Binds Hyprland reports as dispatcher __lua are shown with resolved keys from the source-derived cache and remain dispatchable.' The fretish adds 'the keybindings_menu shall eventually satisfy lua_binds_dispatchable'. A scan that never terminates can never show the binds at all, so the eventual guarantee is violated.

  • Violates SYS-REQ-260922-6642: omarchy-menu-keybindings --print hangs indefinitely: it performs neither its documented side effect (print the binding list) nor a loud, exit-coded refusal.

    Engine: engine-executed run exited 1 and showed the defect signal

    What “confirmed” means here: the requirement exists and the engine reproduced the defect. That the reproducer exercises THIS requirement is the validator's argument (see rationale).

    Evidence: requirement SYS-REQ-260922-6642 · file pocs/reproducers/a withheld known issue/repro.sh

    Validator's rationale (commentary): SYS-REQ-260922-6642: 'Menu action scripts (plugin picker, share, timezone, images, keybindings, file picker, emoji insert) perform their documented side effect or a loud, exit-coded refusal.' The hang is neither - the silent shape the error_handling obligation grades high.

  • ConfirmedReproducible

    Reproducible: A user Hyprland Lua config that iterates hl.get_monitors() with ipairs at load time hangs omarchy-menu-keybindings --print indefinitely; only an external timeout ends it.

    Engine: engine-executed run exited 1 and showed the defect signal

    Evidence: file pocs/reproducers/a withheld known issue/repro.sh

  • ConfirmedAffects a componentmenu

    Affects the menu component

    Engine: confirmed violation of SW-REQ-260922-0W96, SYS-REQ-260922-6642 owned by menu

    Evidence: requirement SW-REQ-260922-0W96 · requirement SYS-REQ-260922-6642 · file pocs/reproducers/a withheld known issue/repro.sh

Engine reference checks at 46fea98725ea (11/11 ok)
  • okcommit46fea98725ea6adcc5fe7d1acbb3542f84f580ec
  • okbase commitecae57a237ef4b084c234a25835b8145ddc5295d
  • okbase ancestryecae57a237ef4b084c234a25835b8145ddc5295d
  • okproduct code untouchedthis report's reproducer branch
  • okbranchthis report's reproducer branch
  • okfilepocs/reproducers/a withheld known issue/repro.sh
  • okrequirementSW-REQ-260922-0W96
  • okrequirementSYS-REQ-260922-6642
  • okcomponentmenu
  • okknown issueKI-MENU-KEYBINDINGS-NO-TIMEOUT
  • okknown issueKI-MENU-KEYBINDINGS-SCAN-DOFILE-ABORT

The engine's own check, at validation time, that the commits, the reproducer branch (touching only reproducer files), the reproducer files and every mapped requirement / component / known issue really exist.

Validator-authored hypothesis. Each claim's verification is engine-computed: confirmed only from an engine-executed reproducer run recorded in this record, refuted when recorded evidence contradicts it. Prose is commentary. The disposition of the report is a human decision.

Reproducer

engine-runexit 1 · expected exit 0 · defect exit 1 · 11022 ms · 2026-10-10T05:03:25Z→ showed the defect
pocs/reproducers/a withheld known issue/repro.shon this report's reproducer branch @ 46fea98725
bash pocs/reproducers/a withheld known issue/repro.sh

Setup failure: exit 2 means the reproducer could not set up (e.g. the product did not build) — such a run decides nothing.

Defect signal: output contains PROOF-REPRODUCER: defect-present a withheld known issue

Matched lines (engine, over the full output)
PROOF-REPRODUCER: defect-present a withheld known issue
Observed output · 143 bytes
running omarchy-menu-keybindings --print with the reported trigger in the user config
exit=124
PROOF-REPRODUCER: defect-present a withheld known issue

Needs: linux · coreutils timeout on PATH · lua interpreter on PATH · ran on linux/amd64 · Runs as a normal user with a throwaway HOME; no Hyprland session is needed because the hang happens in the lua bind scan before any hyprctl call.

Linked

  • Fixed bypr/14128· confirmed
  • Fixed bypr/12712· confirmed
  • Fixed bypr/7564· confirmed
  • Fixed bypr/13276· confirmed
  • Fixed bypr/11733· confirmed
  • Fixed bypr/12155· confirmed

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Decided by agents · verified: in the audited area (menu)
  • Decided by agents · verified: matches no known issue

The outcome (valid, duplicate, not a bug, …) is not decided yet.