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

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

Decided: Out of scope — decided by agents · verified 3 days ago.

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

idle.lock above ~24.8 days overflows the int32 timer ms: 1ms timer storm fills $XDG_RUNTIME_DIR and breaks all app launchingrevision 1 · captured 2026-09-28 21:29 UTC via poll

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