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

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

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

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?
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

omarchy-menu-keybindings lua bind scan spins at 100% CPU when user config iterates a mocked hl APIrevision 1 · captured 2026-10-05 07:56 UTC via poll

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

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):
   __index = function(_, key)
     return type(key) == "number" and nil or noop
   end
  1. 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
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

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-09 09:56 UTC