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.
idle.lock above ~24.8 days overflows the int32 timer ms: 1ms timer storm fills $XDG_RUNTIME_DIR and breaks all app launching
Reported by fsedano · filed Aug 19, 2026 · open at source
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?
- AreaOutside the audited area· 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
Summary
Setting idle.lock in shell.json to a large value meant to express "never lock" — the natural 999999999 — overflows the 32-bit-millisecond Timer.interval of the idle service's lockTimer: (lock − screensaver) × 1000 wraps to a negative millisecond count. Qt clamps to 1 ms and prints WARN: QBasicTimer::start: negative intervals aren't allowed; the interval will be set to 1ms. on every re-arm, which then continues for the entire duration of every idle cycle (~800 lines/s measured). The shell's instance log ($XDG_RUNTIME_DIR/quickshell/by-id/<instance>/log.log) grows ~2.7 MB per 30 s; on my 1.6 GB default /run/user/1000 it filled the tmpfs in ~2.3 h, after which no application could be launched (critical "App failure: Invalid arguments:" on every launch — see system impact below).
Reproduced twice: the original incident (Aug 18 22:50 → Aug 19 05:07, 13.7 M log lines) and a controlled re-pro performed while filing this issue.
Relevant code, shell/plugins/services/idle/Service.qml:
readonly property int lockDelaySeconds: Math.max(0, lockTimeoutSeconds - firstIdleTimeoutSeconds)
...
Timer {
id: lockTimer
interval: root.lockDelaySeconds * 1000With lock: 999999999, screensaver: 150: 999849999 × 1000 = 999,849,999,000 ms, which as an int32 is 999849999000 mod 2^32 = 3,417,586,328 → signed = −877,380,968 ms. Negative interval → QBasicTimer warns and clamps to 1 ms on each start.
Note: the wrapped timer never managed to fire lockSystem once in 2.3 h of flooding (zero lock-system events in that window), so the user is not actually locked — the damage is purely the log flood.
Expected behavior
An out-of-range (or absurdly large) idle value should be clamped to the int32-ms safe range or rejected loudly at config load — not wrap into a broken timer whose warning spam fills the runtime directory.
Reproduction steps
~/.config/omarchy/shell.json→{ "idle": { "screensaver": 150, "lock": 999999999 } }omarchy restart shell- Leave the machine with no input for 150 s
tail -f $XDG_RUNTIME_DIR/quickshell/by-id/<newest>/log.log
Observed (controlled re-pro, this filing):
16:48:39.224 DEBUG qml: omarchy idle ... idle-cycle-start: screensaver=150 lock=999999999
16:48:39.225 WARN: QBasicTimer::start: negative intervals aren't allowed; the interval will be set to 1ms.
16:48:39.227 WARN: QBasicTimer::start: negative intervals aren't allowed; ...First warning lands ~1 ms after idle-cycle-start; ~24,102 warnings in the following 30 s, 2,800 KB of log. The storm persists for the whole idle cycle and resumes on every new cycle; activity cancels the cycle and the storm.
Safe values (lock ≤ 2,147,483 s, i.e. delay × 1000 fits int32) do not warn; the lock timer simply arms for a very long time.
System impact (why this is more than the undocumented ceiling)
#7178 already notes, in its workaround analysis, that the large-value workaround "relies on an undocumented ceiling" (~24.8 days). This report adds the demonstrated, session-breaking consequence of crossing it:
- The instance log fills
/run/user/1000to 100 %. - uwsm's
wayland-wm-app-daemon.servicecrash-loops with[Errno 28] No space left on device. Each restart finds itsapp_daemon_errorflag left truncated/empty by the ENOSPC'd write and immediately replieserror 'Invalid arguments: ' 2to the next request (main.pyapp_daemon(), "argparse exit workaround"). - Every
omarchy launch terminal/browser/ menu launch therefore fails with a critical "App failure: Invalid arguments:" notification. No application can be opened until the log is truncated and the value fixed.
uwsm-side evidence from the incident:
uwsm_app-daemon: [Errno 28] No space left on device
systemd[1022]: wayland-wm-app-daemon.service: Main process exited, status=1/FAILURE (restart counter 2→4)
uwsm_app-daemon: error flag /run/user/1000/uwsm/app_daemon_error exists
uwsm_app-daemon: sent: error 'Invalid arguments: ' 2Incident instance-log evidence (13,673,933 lines total):
2026-08-18 22:50:38.580 DEBUG qml: omarchy idle ... idle-cycle-start: screensaver=150 lock=999999999
2026-08-18 22:50:38.580 DEBUG qml: omarchy idle ... process-start: screensaver ...
2026-08-18 22:50:38.582 WARN: QBasicTimer::start: negative intervals aren't allowed; ...
... ~800 lines/s until activity ...
2026-08-19 05:07:15.682 WARN: QBasicT ← last line, cut mid-write as the tmpfs hit 100 %Workaround (applied on the affected system)
idle.lock ≤ 2,147,483(safe ceiling). I uselock: 2000000(~23 days); verified withomarchy-shell idle status→"lock":2000000,"lockDelay":1999850.- One-time recovery of an affected system:
truncate -s 0the giant instance log(s),omarchy restart shell,systemctl --user restart wayland-wm-app-daemon.service, and confirmomarchy launch terminalworks.
Suggested fixes
- Validate
idle.lock(andidle.screensaver) against the int32-ms ceiling — e.g. reject/clamp anything whose resultingdelay × 1000 > 2,147,483,647— inIdleModel.secondsFromConfigorService.qml. Falling back to the 300 s default (the current "negative value" behaviour) is a footgun for this case; clamping to the ceiling (or logging once at config load, not per re-arm) would be friendlier. - Give a real "never lock" its first-class value (see #7178) so users don't need to reach for giant numbers at all.
- Optional hardening: cap/rotate the quickshell instance log so one misbehaving timer cannot exhaust
$XDG_RUNTIME_DIRby itself.
Related
- #7178 — no way to disable the idle lock; its workaround analysis documents the same ~24.8-day int32 ceiling ("the natural instinct (
99999999) is past the limit").
System details
- Omarchy 4.0.0-1 (stable channel)
- Hyprland 0.56.2, Quickshell 0.3.0 (quickshell-git, revision 28771c7)
- Kernel 7.1.8, 15 GiB RAM, Intel i7-9750H (UHD 630 + RX 5500)
View raw text
## Summary
Setting `idle.lock` in `shell.json` to a large value meant to express "never lock" — the natural `999999999` — overflows the 32-bit-millisecond `Timer.interval` of the idle service's `lockTimer`: `(lock − screensaver) × 1000` wraps to a negative millisecond count. Qt clamps to 1 ms and prints `WARN: QBasicTimer::start: negative intervals aren't allowed; the interval will be set to 1ms.` on every re-arm, which then continues for the **entire duration of every idle cycle** (~800 lines/s measured). The shell's instance log (`$XDG_RUNTIME_DIR/quickshell/by-id/<instance>/log.log`) grows ~2.7 MB per 30 s; on my 1.6 GB default `/run/user/1000` it filled the tmpfs in ~2.3 h, after which **no application could be launched** (critical "App failure: Invalid arguments:" on every launch — see system impact below).
Reproduced twice: the original incident (Aug 18 22:50 → Aug 19 05:07, 13.7 M log lines) and a controlled re-pro performed while filing this issue.
Relevant code, `shell/plugins/services/idle/Service.qml`:
```qml
readonly property int lockDelaySeconds: Math.max(0, lockTimeoutSeconds - firstIdleTimeoutSeconds)
...
Timer {
id: lockTimer
interval: root.lockDelaySeconds * 1000
```
With `lock: 999999999`, `screensaver: 150`: `999849999 × 1000 = 999,849,999,000 ms`, which as an int32 is `999849999000 mod 2^32 = 3,417,586,328` → signed = **−877,380,968 ms**. Negative interval → QBasicTimer warns and clamps to 1 ms on each start.
Note: the wrapped timer never managed to fire `lockSystem` once in 2.3 h of flooding (zero `lock-system` events in that window), so the user is *not* actually locked — the damage is purely the log flood.
## Expected behavior
An out-of-range (or absurdly large) idle value should be clamped to the int32-ms safe range or rejected loudly at config load — not wrap into a broken timer whose warning spam fills the runtime directory.
## Reproduction steps
1. `~/.config/omarchy/shell.json` → `{ "idle": { "screensaver": 150, "lock": 999999999 } }`
2. `omarchy restart shell`
3. Leave the machine with no input for 150 s
4. `tail -f $XDG_RUNTIME_DIR/quickshell/by-id/<newest>/log.log`
Observed (controlled re-pro, this filing):
```
16:48:39.224 DEBUG qml: omarchy idle ... idle-cycle-start: screensaver=150 lock=999999999
16:48:39.225 WARN: QBasicTimer::start: negative intervals aren't allowed; the interval will be set to 1ms.
16:48:39.227 WARN: QBasicTimer::start: negative intervals aren't allowed; ...
```
First warning lands ~1 ms after `idle-cycle-start`; ~24,102 warnings in the following 30 s, 2,800 KB of log. The storm persists for the whole idle cycle and resumes on every new cycle; activity cancels the cycle and the storm.
Safe values (`lock ≤ 2,147,483` s, i.e. delay × 1000 fits int32) do not warn; the lock timer simply arms for a very long time.
## System impact (why this is more than the undocumented ceiling)
#7178 already notes, in its workaround analysis, that the large-value workaround "relies on an undocumented ceiling" (~24.8 days). This report adds the demonstrated, session-breaking consequence of crossing it:
1. The instance log fills `/run/user/1000` to 100 %.
2. uwsm's `wayland-wm-app-daemon.service` crash-loops with `[Errno 28] No space left on device`. Each restart finds its `app_daemon_error` flag left truncated/empty by the ENOSPC'd write and immediately replies `error 'Invalid arguments: ' 2` to the next request (main.py `app_daemon()`, "argparse exit workaround").
3. Every `omarchy launch terminal` / `browser` / menu launch therefore fails with a critical "App failure: Invalid arguments:" notification. No application can be opened until the log is truncated and the value fixed.
uwsm-side evidence from the incident:
```
uwsm_app-daemon: [Errno 28] No space left on device
systemd[1022]: wayland-wm-app-daemon.service: Main process exited, status=1/FAILURE (restart counter 2→4)
uwsm_app-daemon: error flag /run/user/1000/uwsm/app_daemon_error exists
uwsm_app-daemon: sent: error 'Invalid arguments: ' 2
```
Incident instance-log evidence (13,673,933 lines total):
```
2026-08-18 22:50:38.580 DEBUG qml: omarchy idle ... idle-cycle-start: screensaver=150 lock=999999999
2026-08-18 22:50:38.580 DEBUG qml: omarchy idle ... process-start: screensaver ...
2026-08-18 22:50:38.582 WARN: QBasicTimer::start: negative intervals aren't allowed; ...
... ~800 lines/s until activity ...
2026-08-19 05:07:15.682 WARN: QBasicT ← last line, cut mid-write as the tmpfs hit 100 %
```
## Workaround (applied on the affected system)
- `idle.lock ≤ 2,147,483` (safe ceiling). I use `lock: 2000000` (~23 days); verified with `omarchy-shell idle status` → `"lock":2000000,"lockDelay":1999850`.
- One-time recovery of an affected system: `truncate -s 0` the giant instance log(s), `omarchy restart shell`, `systemctl --user restart wayland-wm-app-daemon.service`, and confirm `omarchy launch terminal` works.
## Suggested fixes
- Validate `idle.lock` (and `idle.screensaver`) against the int32-ms ceiling — e.g. reject/clamp anything whose resulting `delay × 1000 > 2,147,483,647` — in `IdleModel.secondsFromConfig` or `Service.qml`. Falling back to the 300 s default (the current "negative value" behaviour) is a footgun for this case; clamping to the ceiling (or logging once at config load, not per re-arm) would be friendlier.
- Give a real "never lock" its first-class value (see #7178) so users don't need to reach for giant numbers at all.
- Optional hardening: cap/rotate the quickshell instance log so one misbehaving timer cannot exhaust `$XDG_RUNTIME_DIR` by itself.
## Related
- #7178 — no way to disable the idle lock; its workaround analysis documents the same ~24.8-day int32 ceiling ("the natural instinct (`99999999`) is past the limit").
## System details
- Omarchy 4.0.0-1 (stable channel)
- Hyprland 0.56.2, Quickshell 0.3.0 (quickshell-git, revision 28771c7)
- Kernel 7.1.8, 15 GiB RAM, Intel i7-9750H (UHD 630 + RX 5500)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/9170· 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.
- Out of scopeDecided by agents · verified · 2026-10-09 02:20 UTC