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-498GitHub issuePublicFixed

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).

Decided: Duplicate — decided by agents · verified 4 days ago.

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?
AreaIn the audited areamenu· Decided by agents · verified
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

Keybindings menu (SUPER+K) hangs forever if hyprland.lua iterates a hyprlua getterrevision 1 · captured 2026-09-28 21:23 UTC via poll

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)'
1000001

This 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 + 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

Omarchy4.0.3-1
Hyprland0.56.2 (commit efb5099)
CPUIntel Core 5 210H
GPUIntel Raptor Lake-P iGPU + NVIDIA RTX 4050 Max-Q
KernelLinux 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

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.

  • DuplicateR-1816Decided by agents · verified · 2026-10-08 22:18 UTC