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 config scan hangs forever (100% CPU) if hyprland.lua calls any un-stubbed hl.* function inside ipairs()
Reported by LukaszWituch · filed Aug 28, 2026Closed upstream on 2026-09-20 by bjarneo via an automated message, as a duplicate of #7025 (R-1816). GitHub close reason: completed.
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?
- Not yet mapped to any requirement or component.
- 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?
- Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. No decision has been recorded yet.
Original report
What happened
Opening the keybindings menu (SUPER+K) spawns a lua process that never
exits and pins one CPU core at ~100% indefinitely. Every SUPER+K press
(when the keybindings cache misses) spawns another one, so repeated
presses stack up multiple runaway processes.
Root cause
omarchy-menu-keybindings's build_lua_bind_cache() runs a lua heredoc
that dofile()s the user's ~/.config/hypr/hyprland.lua chain against a
mocked hl table, to discover Lua-registered binds without actually
talking to Hyprland. The mock's fallback object is:
local noop
noop = setmetatable({}, {
__index = function()
return noop
end,
__call = function()
return noop
end,
})Any hl.* field not explicitly stubbed (only dsp, bind, andget_config are) resolves through __index to noop, and calling it
also returns noop — always truthy, never nil.
My ~/.config/hypr/monitors.lua (an ordinary, working config used to
pick a monitor layout based on which dock is connected) contains:
for _, m in ipairs(hl.get_monitors()) doUnder real Hyprland this is fine. Under the mock, hl.get_monitors()
returns noop, and ipairs(noop) walks noop[1], noop[2], noop[3], …
via __index — which never returns nil — so the loop never terminates.
This isn't specific to get_monitors: any config that calls an
un-stubbed hl.* function and iterates its result with ipairs/pairs
will hang the same way.
Expected
The keybindings menu's background scan should either finish quickly or
fail safely, without ever spinning a CPU core forever.
Steps to reproduce
- In
~/.config/hypr/monitors.lua(or any file in thehyprland.lua
require chain), add:
for _, m in ipairs(hl.get_monitors()) do end
2. Press SUPER+K (or run omarchy-menu-keybindings) once the keybindings
cache is stale/empty.
3. Observe a lua child process (parent: omarchy-menu-keybindings)
pinned at ~100% CPU that never exits.
Suggested fix
Make the mock's numeric indexing terminate instead of looping forever,
e.g.:
__index = function(_, key)
if type(key) == "number" then return nil end
return noop
end,This lets ipairs() over any un-stubbed hl.* return value stop after
zero iterations, while leaving string-keyed chaining (hl.dsp.exec_cmd,
etc.) untouched.
System
- Omarchy version: 4.0.1-1
- Reproduced via a real user config, not a synthetic minimal case
Filed by Claude Sonnet 5 via Claude Code.
View raw text
### What happened
Opening the keybindings menu (SUPER+K) spawns a `lua` process that never
exits and pins one CPU core at ~100% indefinitely. Every SUPER+K press
(when the keybindings cache misses) spawns another one, so repeated
presses stack up multiple runaway processes.
### Root cause
`omarchy-menu-keybindings`'s `build_lua_bind_cache()` runs a `lua` heredoc
that `dofile()`s the user's `~/.config/hypr/hyprland.lua` chain against a
mocked `hl` table, to discover Lua-registered binds without actually
talking to Hyprland. The mock's fallback object is:
```lua
local noop
noop = setmetatable({}, {
__index = function()
return noop
end,
__call = function()
return noop
end,
})
```
Any `hl.*` field not explicitly stubbed (only `dsp`, `bind`, and
`get_config` are) resolves through `__index` to `noop`, and calling it
also returns `noop` — always truthy, never `nil`.
My `~/.config/hypr/monitors.lua` (an ordinary, working config used to
pick a monitor layout based on which dock is connected) contains:
```lua
for _, m in ipairs(hl.get_monitors()) do
```
Under real Hyprland this is fine. Under the mock, `hl.get_monitors()`
returns `noop`, and `ipairs(noop)` walks `noop[1]`, `noop[2]`, `noop[3]`, …
via `__index` — which never returns `nil` — so the loop never terminates.
This isn't specific to `get_monitors`: any config that calls an
un-stubbed `hl.*` function and iterates its result with `ipairs`/`pairs`
will hang the same way.
### Expected
The keybindings menu's background scan should either finish quickly or
fail safely, without ever spinning a CPU core forever.
### Steps to reproduce
1. In `~/.config/hypr/monitors.lua` (or any file in the `hyprland.lua`
require chain), add:
`for _, m in ipairs(hl.get_monitors()) do end`
2. Press SUPER+K (or run `omarchy-menu-keybindings`) once the keybindings
cache is stale/empty.
3. Observe a `lua` child process (parent: `omarchy-menu-keybindings`)
pinned at ~100% CPU that never exits.
### Suggested fix
Make the mock's numeric indexing terminate instead of looping forever,
e.g.:
```lua
__index = function(_, key)
if type(key) == "number" then return nil end
return noop
end,
```
This lets `ipairs()` over any un-stubbed `hl.*` return value stop after
zero iterations, while leaving string-keyed chaining (`hl.dsp.exec_cmd`,
etc.) untouched.
### System
- Omarchy version: 4.0.1-1
- Reproduced via a real user config, not a synthetic minimal case
Filed by Claude Sonnet 5 via Claude Code.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
- Fixed bypr/12712· confirmed
- Fixed bypr/7564· 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.
No decision has been recorded yet.