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.
Keybindings menu (SUPER+K) hangs forever if hyprland.lua iterates a hyprlua getter
Reported by L0nE-F0x · filed Sep 16, 2026Closed upstream on 2026-10-12 by the bot omarchybot, as a duplicate (target not recorded).
R-1816 is the earliest report of this defect — filed Aug 15, 2026, open upstream. This report (filed Sep 16, 2026) describes the same defect, so it is folded under it in the inbox.
Decided by agents · verified· Oct 8, 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
What happened
Any user hyprland.lua that iterates a hyprlua getter — e.g. ipairs(hl.get_windows()) — permanently and silently breaks the keybindings menu (SUPER + K). The menu never opens, nothing is logged, and hyprctl configerrors stays clean, so there is no trail back to the offending config.
Why
omarchy-menu-keybindings re-executes ~/.config/hypr/hyprland.lua under a stub hl in build_lua_bind_cache(), to recover descriptions for binds that Hyprland still reports as dispatcher __lua. The stub's fallback object is:
local noop
noop = setmetatable({}, {
__index = function() return noop end,
__call = function() return noop end,
})hl's own metatable returns this noop for every member it does not explicitly define, so hl.get_windows() returns noop. Since __index answers every key with a non-nil value, ipairs(noop) never reaches a nil and loops forever:
$ lua -e 'local n; n = setmetatable({}, {__index=function() return n end})
local i=0; for _ in ipairs(n) do i=i+1; if i>1000000 then break end end
print(i)'
1000001This affects every unstubbed getter — get_windows, get_workspaces, get_workspace_windows, get_monitors, etc.
Reproduction
mkdir -p /tmp/fakehome/.config/hypr
cat > /tmp/fakehome/.config/hypr/hyprland.lua <<'LUA'
for _, w in ipairs(hl.get_windows() or {}) do print(w.title) end
LUA
timeout 20 env HOME=/tmp/fakehome omarchy-menu-keybindings --print; echo "exit=$?"Result: exit=124 (hung). Remove the loop from that config and the same command returns in 0.011s. Pressing SUPER + K on a real config containing such a loop reproduces it identically.
Impact
- Permanent, not transient. The scan hangs before writing
~/.cache/omarchy/keybindings-*.records, so no cache is ever produced to fall back on.SUPER + Kstays broken until the config is found and edited — and the failure gives no hint which file is responsible. - Each press leaks a spinning process. Every
SUPER + Kleaves twoomarchy-menu-keybindingsbash processes plus aluachild pegged at 100% CPU. I had five stuck instances and two runawayluaprocesses before noticing; load average was 3.84 on an idle desktop. - The `lua` child outlives its parent.
pkill -f omarchy-menu-keybindingsreaps the bash processes but orphans thelua, which keeps spinning and must be killed by PID. - Because the processes are children of the shell service, the CPU burn is attributed to
omarchy-shell.service, which points diagnosis away from the real cause.
Suggested fix
Either of these alone closes the loop; together they also bound the damage from anything else the stub cannot anticipate.
- Terminate `ipairs` on the stub — have
__indexreturnnilfor positive integer keys, so an array iteration yields zero items instead of running forever. - Return real empty arrays for the known getters — stub
get_windows,get_workspaces,get_workspace_windowsandget_monitorsasfunction() return {} end, which is also what a correct scan wants. - Bound the scan — wrap the
luaheredoc intimeoutso a pathological config degrades to "descriptions missing" rather than "menu never opens and a core spins".
A timeout alone would have turned this from a silent permanent breakage into a visibly degraded menu, which is a much easier bug to report.
Discovered via
A third-party Hyprland Lua overlay (window-placement rules for a game companion app) sourced from the tail of hyprland.lua. Fixed on that side by guarding the iteration with rawget(value, 1) == nil, which bypasses the stub's __index while leaving real hyprlua arrays untouched — but any config doing the obvious thing hits this.
System
| Omarchy | 4.0.3-1 |
| Hyprland | 0.56.2 (commit efb5099) |
| CPU | Intel Core 5 210H |
| GPU | Intel Raptor Lake-P iGPU + NVIDIA RTX 4050 Max-Q |
| Kernel | Linux 7.2.3-arch1-3 |
(omarchy debug is referenced in the docs but is not a command in 4.0.3, so the above is collected by hand.)
View raw text
## What happened
Any user `hyprland.lua` that iterates a hyprlua getter — e.g. `ipairs(hl.get_windows())` — permanently and silently breaks the keybindings menu (`SUPER + K`). The menu never opens, nothing is logged, and `hyprctl configerrors` stays clean, so there is no trail back to the offending config.
## Why
`omarchy-menu-keybindings` re-executes `~/.config/hypr/hyprland.lua` under a stub `hl` in `build_lua_bind_cache()`, to recover descriptions for binds that Hyprland still reports as dispatcher `__lua`. The stub's fallback object is:
```lua
local noop
noop = setmetatable({}, {
__index = function() return noop end,
__call = function() return noop end,
})
```
`hl`'s own metatable returns this `noop` for every member it does not explicitly define, so `hl.get_windows()` returns `noop`. Since `__index` answers **every** key with a non-nil value, `ipairs(noop)` never reaches a `nil` and loops forever:
```console
$ lua -e 'local n; n = setmetatable({}, {__index=function() return n end})
local i=0; for _ in ipairs(n) do i=i+1; if i>1000000 then break end end
print(i)'
1000001
```
This affects every unstubbed getter — `get_windows`, `get_workspaces`, `get_workspace_windows`, `get_monitors`, etc.
## Reproduction
```bash
mkdir -p /tmp/fakehome/.config/hypr
cat > /tmp/fakehome/.config/hypr/hyprland.lua <<'LUA'
for _, w in ipairs(hl.get_windows() or {}) do print(w.title) end
LUA
timeout 20 env HOME=/tmp/fakehome omarchy-menu-keybindings --print; echo "exit=$?"
```
Result: `exit=124` (hung). Remove the loop from that config and the same command returns in `0.011s`. Pressing `SUPER + K` on a real config containing such a loop reproduces it identically.
## Impact
* **Permanent, not transient.** The scan hangs *before* writing `~/.cache/omarchy/keybindings-*.records`, so no cache is ever produced to fall back on. `SUPER + K` stays broken until the config is found and edited — and the failure gives no hint which file is responsible.
* **Each press leaks a spinning process.** Every `SUPER + K` leaves two `omarchy-menu-keybindings` bash processes plus a `lua` child pegged at 100% CPU. I had five stuck instances and two runaway `lua` processes before noticing; load average was 3.84 on an idle desktop.
* **The `lua` child outlives its parent.** `pkill -f omarchy-menu-keybindings` reaps the bash processes but orphans the `lua`, which keeps spinning and must be killed by PID.
* Because the processes are children of the shell service, the CPU burn is attributed to `omarchy-shell.service`, which points diagnosis away from the real cause.
## Suggested fix
Either of these alone closes the loop; together they also bound the damage from anything else the stub cannot anticipate.
1. **Terminate `ipairs` on the stub** — have `__index` return `nil` for positive integer keys, so an array iteration yields zero items instead of running forever.
2. **Return real empty arrays for the known getters** — stub `get_windows`, `get_workspaces`, `get_workspace_windows` and `get_monitors` as `function() return {} end`, which is also what a correct scan wants.
3. **Bound the scan** — wrap the `lua` heredoc in `timeout` so a pathological config degrades to "descriptions missing" rather than "menu never opens and a core spins".
A `timeout` alone would have turned this from a silent permanent breakage into a visibly degraded menu, which is a much easier bug to report.
## Discovered via
A third-party Hyprland Lua overlay (window-placement rules for a game companion app) sourced from the tail of `hyprland.lua`. Fixed on that side by guarding the iteration with `rawget(value, 1) == nil`, which bypasses the stub's `__index` while leaving real hyprlua arrays untouched — but any config doing the obvious thing hits this.
## System
| | |
|---|---|
| Omarchy | 4.0.3-1 |
| Hyprland | 0.56.2 (commit `efb5099`) |
| CPU | Intel Core 5 210H |
| GPU | Intel Raptor Lake-P iGPU + NVIDIA RTX 4050 Max-Q |
| Kernel | Linux 7.2.3-arch1-3 |
(`omarchy debug` is referenced in the docs but is not a command in 4.0.3, so the above is collected by hand.)
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/14128· confirmed
- Duplicate ofR-1816omarchy-menu-keybindings hangs forever when a user Lua config iterates hl.get_monitors()· decided by agents · verified
- Fixed bypr/12712· confirmed
- Related PRpr/12916· 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.