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-1659GitHub issuePublicReceived

omarchy-shell lock screen crash-loops (~every 18s) after locking, due to DPMS-off

Reported by ultgames · filed Aug 19, 2026 · open at source

In the audited area (lock) — decided by agents · verified. Not validated yet.

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 arealock· 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?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: in the audited area (lock) — decided by agents · verified; matches no known issue — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.

Original report

omarchy-shell lock screen crash-loops (~every 18s) after locking, due to DPMS-offrevision 1 · captured 2026-09-28 21:29 UTC via poll

System details

AMD Radeon RX 570/580/590 Omarchy 4.0

What's wrong?

Summary

After the Quattro upgrade, the omarchy-shell lock screen crashes with a Wayland/EGL
fatal error almost every time it needs to create its lock surface — both on resume
from suspend, and on a normal idle-timeout lock. It usually self-recovers within a
few seconds via the shell's own "lock-stranded: recovering" logic, but recovery is
not guaranteed to land in time, and when it doesn't, Hyprland falls through to its
native crashed-lockscreen fallback screen ("Oopsie daisy, it looks like you locked
your screen but the lockscreen app died"), requiring TTY-based recovery
(hyprctl --instance 0 eval 'hl.clear_crashed_lockscreen()' or killall -9 hyprlock).

This looks related to older pre-Quattro hyprlock crash-on-resume reports (#5935,
#5521), but those were against direct hyprlock invocation. Quattro replaced that
with a shell-owned lock (omarchy-shell lock lock via WlSessionLock), and the
same class of "EGL context not ready" race reappears through the new code path.
I couldn't find an existing issue against the Quattro/omarchy-shell architecture
specifically, so filing this as (possibly) new.

Crash signature (from journalctl)

omarchy-shell[PID]: WARN qt.qpa.wayland: Could not create EGL surface (EGL error 0x3000)
omarchy-shell[PID]: WARN qt.qpa.wayland: eglSwapBuffers failed with 0x300d, surface: 0x0
omarchy-shell[PID]: WARN: The Wayland connection experienced a fatal error: Invalid argument
omarchy-shell[PID2]: Omarchy shell exited with status 255; relaunching.
omarchy-shell[PID3]: DEBUG qml: omarchy lock <timestamp> lock-stranded: recovering

Reproduction / frequency

On the boot that started right after the Quattro upgrade (2026-08-15 14:59) through
2026-08-19, out of 5 suspend/resume cycles, 4 produced this exact crash sequence on
resume. All 4 self-recovered within a few seconds, but at least one occurrence did
not recover in time and produced the native Hyprland "lockscreen app died" fallback
screen, requiring a TTY switch to recover.

Separately, the same crash also fires on a normal **idle-timeout lock with no
suspend involved**, if the screensaver is disabled
(omarchy-toggle-screensaver). Root cause for that path, traced in
shell/plugins/services/idle/Service.qml:

  • shell.json's idle.screensaver (default 150s) and idle.lock (default 300s)

both start counting from the same idle-start event.
- screensaverDelaySeconds is computed as `screensaverTimeoutSeconds -
min(screensaverTimeoutSeconds, lockTimeoutSeconds)`, so the screensaver launch
always fires at the 150s mark regardless of whether it's enabled.
- With the screensaver enabled, that puts a terminal window on screen which keeps
the compositor actively rendering until the 300s lock timer fires.
- With the screensaver disabled, omarchy-launch-screensaver exits instantly
(early-exits on the screensaver-off toggle check), so the compositor sits fully
idle with nothing rendering for the entire 150s→300s window — leaving it just as
"cold" as a post-suspend compositor by the time the lock timer fires and
omarchy-system-lock → omarchy-shell lock lock tries to create the
WlSessionLockSurface.
- Confirmed via journal timestamps on 2026-08-19: idle-cycle-start at 11:21:12 →
screensaver process exitCode=1 at 11:21:13 (toggled off, no window ever opens)
→ crash (Wayland connection experienced a fatal error) at 11:23:59 — ~167s
later, matching the 150s lock-delay window closely enough to not be coincidence.

Re-enabling the screensaver eliminates this second (idle-timeout) trigger, since it
keeps the compositor warm during the wait. It does not affect the
resume-from-suspend trigger, which still reproduces regardless of screensaver state.

Likely proximate cause

In shell/plugins/lock/Service.qml, sessionLockStabilizeTimer is a **hardcoded
500ms** Timer that fires after a Quickshell.screens change (e.g. resume, or a
lock request) and then calls requestSessionLock(), which sets
sessionLock.locked = true — this is what causes Quickshell to create the
WlSessionLockSurface, and that EGL surface creation is what's failing. 500ms may
not be enough time for amdgpu/Mesa to have the output pipeline fully ready after
outputs have just come back from being off/idle, and there's no shell.json
config knob exposed for this delay — increasing it, and/or retrying EGL surface
creation instead of letting the whole shell process crash, seem like the likely
fix directions.

Workaround tried

Re-enabling the screensaver (omarchy-toggle-screensaver) fixes the idle-lock
trigger but not the resume-from-suspend trigger.

Recovery when it happens

# from another TTY (ctrl+alt+F[N])
hyprctl --instance 0 eval 'hl.clear_crashed_lockscreen()'
# if that doesn't help:
killall -9 hyprlock
View raw text
### System details

AMD Radeon RX 570/580/590 Omarchy 4.0

### What's wrong?

## Summary

After the Quattro upgrade, the `omarchy-shell` lock screen crashes with a Wayland/EGL
fatal error almost every time it needs to create its lock surface — both on resume
from suspend, and on a normal idle-timeout lock. It usually self-recovers within a
few seconds via the shell's own "lock-stranded: recovering" logic, but recovery is
not guaranteed to land in time, and when it doesn't, Hyprland falls through to its
native crashed-lockscreen fallback screen ("Oopsie daisy, it looks like you locked
your screen but the lockscreen app died"), requiring TTY-based recovery
(`hyprctl --instance 0 eval 'hl.clear_crashed_lockscreen()'` or `killall -9 hyprlock`).

This looks related to older pre-Quattro hyprlock crash-on-resume reports (#5935,
#5521), but those were against direct hyprlock invocation. Quattro replaced that
with a shell-owned lock (`omarchy-shell lock lock` via `WlSessionLock`), and the
same class of "EGL context not ready" race reappears through the new code path.
I couldn't find an existing issue against the Quattro/omarchy-shell architecture
specifically, so filing this as (possibly) new.

## Crash signature (from `journalctl`)

```
omarchy-shell[PID]: WARN qt.qpa.wayland: Could not create EGL surface (EGL error 0x3000)
omarchy-shell[PID]: WARN qt.qpa.wayland: eglSwapBuffers failed with 0x300d, surface: 0x0
omarchy-shell[PID]: WARN: The Wayland connection experienced a fatal error: Invalid argument
omarchy-shell[PID2]: Omarchy shell exited with status 255; relaunching.
omarchy-shell[PID3]: DEBUG qml: omarchy lock <timestamp> lock-stranded: recovering
```

## Reproduction / frequency

On the boot that started right after the Quattro upgrade (2026-08-15 14:59) through
2026-08-19, out of 5 suspend/resume cycles, 4 produced this exact crash sequence on
resume. All 4 self-recovered within a few seconds, but at least one occurrence did
not recover in time and produced the native Hyprland "lockscreen app died" fallback
screen, requiring a TTY switch to recover.

Separately, the same crash also fires on a normal **idle-timeout lock with no
suspend involved**, if the screensaver is disabled
(`omarchy-toggle-screensaver`). Root cause for that path, traced in
`shell/plugins/services/idle/Service.qml`:

- `shell.json`'s `idle.screensaver` (default 150s) and `idle.lock` (default 300s)
  both start counting from the same idle-start event.
- `screensaverDelaySeconds` is computed as `screensaverTimeoutSeconds -
  min(screensaverTimeoutSeconds, lockTimeoutSeconds)`, so the screensaver launch
  always fires at the 150s mark **regardless of whether it's enabled**.
- With the screensaver enabled, that puts a terminal window on screen which keeps
  the compositor actively rendering until the 300s lock timer fires.
- With the screensaver **disabled**, `omarchy-launch-screensaver` exits instantly
  (early-exits on the `screensaver-off` toggle check), so the compositor sits fully
  idle with nothing rendering for the entire 150s→300s window — leaving it just as
  "cold" as a post-suspend compositor by the time the lock timer fires and
  `omarchy-system-lock` → `omarchy-shell lock lock` tries to create the
  `WlSessionLockSurface`.
- Confirmed via journal timestamps on 2026-08-19: `idle-cycle-start` at 11:21:12 →
  screensaver process `exitCode=1` at 11:21:13 (toggled off, no window ever opens)
  → crash (`Wayland connection experienced a fatal error`) at 11:23:59 — ~167s
  later, matching the 150s lock-delay window closely enough to not be coincidence.

Re-enabling the screensaver eliminates this second (idle-timeout) trigger, since it
keeps the compositor warm during the wait. It does **not** affect the
resume-from-suspend trigger, which still reproduces regardless of screensaver state.

## Likely proximate cause

In `shell/plugins/lock/Service.qml`, `sessionLockStabilizeTimer` is a **hardcoded
500ms** `Timer` that fires after a `Quickshell.screens` change (e.g. resume, or a
lock request) and then calls `requestSessionLock()`, which sets
`sessionLock.locked = true` — this is what causes Quickshell to create the
`WlSessionLockSurface`, and that EGL surface creation is what's failing. 500ms may
not be enough time for amdgpu/Mesa to have the output pipeline fully ready after
outputs have just come back from being off/idle, and there's no `shell.json`
config knob exposed for this delay — increasing it, and/or retrying EGL surface
creation instead of letting the whole shell process crash, seem like the likely
fix directions.

## Workaround tried

Re-enabling the screensaver (`omarchy-toggle-screensaver`) fixes the idle-lock
trigger but not the resume-from-suspend trigger.

## Recovery when it happens

```bash
# from another TTY (ctrl+alt+F[N])
hyprctl --instance 0 eval 'hl.clear_crashed_lockscreen()'
# if that doesn't help:
killall -9 hyprlock
```

Also reported as

This defect is tracked under R-1659 (filed Aug 19, 2026). 10 other reports describe the same defect.

Earliest report of this defect: R-1882 (filed Aug 8, 2026).

  1. Aug 19, 2026R-1659 · this report— tracked here
  2. ·Decided by agents · verified

    Open upstream.

  3. · via R-709·Decided by agents · verified

    Open upstream.

  4. ·Decided by agents · verified

    Open upstream.

  5. ·Decided by agents · verified

    Open upstream.

  6. ·Decided by agents · verified

    Open upstream.

  7. ·Decided by arbitration

    Open upstream.

  8. ·Decided by agents · verified

    Open upstream.

  9. ·Decided by agents · verified

    Open upstream.

  10. · via R-709·Decided by agents · verified

    Open upstream.

  11. ·Decided by agents · verified

    Open upstream.

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

  • Related PRpr/6642· 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.

  • Decided by agents · verified: in the audited area (lock)
  • Decided by agents · verified: matches no known issue

The outcome (valid, duplicate, not a bug, …) is not decided yet.