Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA 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.
omarchy-menu-keybindings lua bind scan spins at 100% CPU when user config iterates a mocked hl API
Reported by zz8011 · filed Oct 5, 2026 · open at source
R-1816 is the earliest report of this defect — filed Aug 15, 2026, open upstream. This report (filed Oct 5, 2026) describes the same defect, so it is folded under it in the inbox.
Decided by agents · verified· Oct 9, 2026
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
Summary
Opening the keybindings menu (omarchy-menu-keybindings) can spawn a lua subprocess that spins at 100% CPU forever, hanging the menu and pegging a core (observed 10+ minutes, package temp up to 96°C). The spinner is build_lua_bind_cache's mock-environment scan of the user's ~/.config/hypr/hyprland.lua.
Root cause
build_lua_bind_cache executes the user's real hyprland.lua against a mock hl table whose __index falls back to a noop sentinel table:
local noop
noop = setmetatable({}, {
__index = function() return noop end,
__call = function() return noop end,
})noop's __index returns noop for any key — including numeric keys. So any user config that iterates over a real Hyprland Lua API that the mock doesn't implement (e.g. hl.get_loaded_plugins() used by the CUA plugin setup) gets noop back, and:
for _, plugin in ipairs(hl.get_loaded_plugins()) do ... endnever terminates: ipairs stops when t[n] is nil, but noop[1], noop[2], … all resolve to noop via __index. Pure CPU spin, no syscalls (confirmed with strace).
Minimal repro
sed -n '79,232p' /usr/share/omarchy/bin/omarchy-menu-keybindings > /tmp/scan.lua
# add `for _ in ipairs(hl.get_loaded_plugins()) do end` to ~/.config/hypr/hyprland.lua
timeout 5 lua /tmp/scan.lua # exit=124, hangsConfirmed on stock 4.0.4: the scan prints all registered binds (236 lines) then spins in the user-config loop. Reverting the ipairs loop to a #-bounded numeric loop makes the scan exit normally.
Suggested fix
Two options, either works:
noop.__indexreturnsnilfor numeric keys (preserves method-chaining, letsipairs/nextterminate):
__index = function(_, key)
return type(key) == "number" and nil or noop
end- Explicitly stub real
hlAPIs in the mock (hl.get_loaded_plugins = function() return {} end).
Secondary observation
The mock defines hl but not o (window-rule API). User configs calling o.window(...) fail under the scan — silently swallowed by pcall, so window rules just don't appear in the menu. Not a hang, but same class of gap.
Environment
- Omarchy 4.0.4-1 (package
omarchy-menu-keybindings) - Hyprland 0.56.2
- Lua 5.5.1
View raw text
## Summary
Opening the keybindings menu (`omarchy-menu-keybindings`) can spawn a `lua` subprocess that spins at 100% CPU **forever**, hanging the menu and pegging a core (observed 10+ minutes, package temp up to 96°C). The spinner is `build_lua_bind_cache`'s mock-environment scan of the user's `~/.config/hypr/hyprland.lua`.
## Root cause
`build_lua_bind_cache` executes the user's real `hyprland.lua` against a mock `hl` table whose `__index` falls back to a `noop` sentinel table:
```lua
local noop
noop = setmetatable({}, {
__index = function() return noop end,
__call = function() return noop end,
})
```
`noop`'s `__index` returns `noop` for **any** key — including numeric keys. So any user config that iterates over a real Hyprland Lua API that the mock doesn't implement (e.g. `hl.get_loaded_plugins()` used by the CUA plugin setup) gets `noop` back, and:
```lua
for _, plugin in ipairs(hl.get_loaded_plugins()) do ... end
```
never terminates: `ipairs` stops when `t[n]` is `nil`, but `noop[1]`, `noop[2]`, … all resolve to `noop` via `__index`. Pure CPU spin, no syscalls (confirmed with `strace`).
## Minimal repro
```bash
sed -n '79,232p' /usr/share/omarchy/bin/omarchy-menu-keybindings > /tmp/scan.lua
# add `for _ in ipairs(hl.get_loaded_plugins()) do end` to ~/.config/hypr/hyprland.lua
timeout 5 lua /tmp/scan.lua # exit=124, hangs
```
Confirmed on stock 4.0.4: the scan prints all registered binds (236 lines) then spins in the user-config loop. Reverting the `ipairs` loop to a `#`-bounded numeric loop makes the scan exit normally.
## Suggested fix
Two options, either works:
1. `noop.__index` returns `nil` for numeric keys (preserves method-chaining, lets `ipairs`/`next` terminate):
```lua
__index = function(_, key)
return type(key) == "number" and nil or noop
end
```
2. Explicitly stub real `hl` APIs in the mock (`hl.get_loaded_plugins = function() return {} end`).
## Secondary observation
The mock defines `hl` but not `o` (window-rule API). User configs calling `o.window(...)` fail under the scan — silently swallowed by `pcall`, so window rules just don't appear in the menu. Not a hang, but same class of gap.
## Environment
- Omarchy 4.0.4-1 (package `omarchy-menu-keybindings`)
- Hyprland 0.56.2
- Lua 5.5.1
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
- Duplicate ofR-1816omarchy-menu-keybindings hangs forever when a user Lua config iterates hl.get_monitors()· decided by agents · verified
- Fixed bypr/12712· confirmed
- Fixed bypr/13276· confirmed
- Fixed bypr/11733· confirmed
- Fixed bypr/12155· confirmed
- Known issueLua bind scan aborts on the first throwing require; every later Lua bind silently vanishes from the menu cache KI-MENU-KEYBINDINGS-SCAN-DOFILE-ABORT· decided by agents · verified
- Fixed bypr/7564· confirmed
- Related PRpr/14128· 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.