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-744GitHub issuePublicDecided· Out of scope

Screensaver busy-loops and cannot be dismissed when branding/screensaver.txt is missing

Reported by hhammarstrand · filed Sep 10, 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

Screensaver busy-loops and cannot be dismissed when branding/screensaver.txt is missingrevision 1 · captured 2026-09-28 21:23 UTC via poll

What happened

If ~/.config/omarchy/branding/screensaver.txt is missing, the screensaver
turns into an unclosable fullscreen window spewing an error message as fast as
the terminal can render it:

Error reading input file: No such file or directory (os error 2)

Escape and every other key are ignored. The only way out is Ctrl+C. To a user
who has just been idle for 150 seconds, this looks exactly like a system crash.

Root cause

omarchy-screensaver (omarchy 4.0.2) has three issues that compound:

while true; do
  ttfx -i ~/.config/omarchy/branding/screensaver.txt ... &   # exits 1 immediately

  while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do       # never entered
    if read -n1 -t 1 || ! screensaver_in_focus; then         # keyboard poll
      exit_screensaver
    fi
  done
done
  1. No existence check on the input file, and no fallback — even though

Omarchy ships an identical file at /usr/share/omarchy/logo.txt.
2. `ttfx` failure is not detected. It exits 1 instantly, so the inner
while pgrep loop never runs a single iteration and while true
relaunches it immediately. This busy-loops at full CPU.
3. The keyboard poll is unreachable. read -n1 lives inside that inner
loop, so nothing reads input. Only trap exit_screensaver SIGINT survives,
which is why Ctrl+C is the sole escape.

Point 3 is the serious one: any failure of ttfx makes the screensaver
impossible to dismiss by normal means.

Measured

I replicated the loop structure against a real pty, counting outer iterations
and entries into the inner while pgrep loop over 3 seconds:

Input fileOuter iterationsInner iterations (keyboard poll)
valid127
missing (run 1)1730
missing (run 2)1310
missing (run 3)1840

With a valid file the keyboard poll runs ~9x/second and Escape dismisses the
screensaver. With the file missing the inner loop is never entered even once,
across three independent runs, while ttfx is relaunched ~55 times per second.

ttfx fails on file open before it does any terminal setup, so it is reliably
dead before pgrep is spawned. This is not a narrow race that sometimes goes
the other way.

Steps to reproduce

mv ~/.config/omarchy/branding/screensaver.txt{,.bak}
omarchy-launch-screensaver force

The screen fills with the repeated error. Escape does nothing. Ctrl+C exits.

Expected

The screensaver should fall back to /usr/share/omarchy/logo.txt when the user
file is absent, and should exit rather than loop if ttfx fails to start.
Dismissing it with a keypress should work regardless.

Suggested fix

Any one of these closes the trap; the first two together would be ideal:

  • Fall back to /usr/share/omarchy/logo.txt if the user file does not exist.
  • Break out of the outer loop when ttfx exits non-zero, instead of relaunching.
  • Move the keyboard poll so it runs even when ttfx is not alive.

How we hit it

Not a hand-crafted repro. ~/.config/omarchy/branding was a symlink into a
dotfiles repo; the repo dropped that directory because its contents were
byte-identical to Omarchy's own logo.txt and icon.txt, on the assumption
that Omarchy would fall back to its own defaults. It does not.

System details

Omarchy4.0.2-1
ttfx0.3.2-1
Kernel6.18.46-1-lts
CPUIntel i7-4770HQ (Haswell)
GPUIntel Iris Pro 5200 (Gen7.5, crocus)
HardwareMacBookPro11,4
View raw text
### What happened

If `~/.config/omarchy/branding/screensaver.txt` is missing, the screensaver
turns into an unclosable fullscreen window spewing an error message as fast as
the terminal can render it:

```
Error reading input file: No such file or directory (os error 2)
```

Escape and every other key are ignored. The only way out is `Ctrl+C`. To a user
who has just been idle for 150 seconds, this looks exactly like a system crash.

### Root cause

`omarchy-screensaver` (omarchy 4.0.2) has three issues that compound:

```bash
while true; do
  ttfx -i ~/.config/omarchy/branding/screensaver.txt ... &   # exits 1 immediately

  while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do       # never entered
    if read -n1 -t 1 || ! screensaver_in_focus; then         # keyboard poll
      exit_screensaver
    fi
  done
done
```

1. **No existence check** on the input file, and no fallback — even though
   Omarchy ships an identical file at `/usr/share/omarchy/logo.txt`.
2. **`ttfx` failure is not detected.** It exits 1 instantly, so the inner
   `while pgrep` loop never runs a single iteration and `while true`
   relaunches it immediately. This busy-loops at full CPU.
3. **The keyboard poll is unreachable.** `read -n1` lives inside that inner
   loop, so nothing reads input. Only `trap exit_screensaver SIGINT` survives,
   which is why `Ctrl+C` is the sole escape.

Point 3 is the serious one: any failure of `ttfx` makes the screensaver
impossible to dismiss by normal means.

### Measured

I replicated the loop structure against a real pty, counting outer iterations
and entries into the inner `while pgrep` loop over 3 seconds:

| Input file | Outer iterations | Inner iterations (keyboard poll) |
|---|---|---|
| valid | 1 | **27** |
| missing (run 1) | 173 | **0** |
| missing (run 2) | 131 | **0** |
| missing (run 3) | 184 | **0** |

With a valid file the keyboard poll runs ~9x/second and Escape dismisses the
screensaver. With the file missing the inner loop is never entered even once,
across three independent runs, while `ttfx` is relaunched ~55 times per second.

`ttfx` fails on file open before it does any terminal setup, so it is reliably
dead before `pgrep` is spawned. This is not a narrow race that sometimes goes
the other way.

### Steps to reproduce

```bash
mv ~/.config/omarchy/branding/screensaver.txt{,.bak}
omarchy-launch-screensaver force
```

The screen fills with the repeated error. Escape does nothing. `Ctrl+C` exits.

### Expected

The screensaver should fall back to `/usr/share/omarchy/logo.txt` when the user
file is absent, and should exit rather than loop if `ttfx` fails to start.
Dismissing it with a keypress should work regardless.

### Suggested fix

Any one of these closes the trap; the first two together would be ideal:

- Fall back to `/usr/share/omarchy/logo.txt` if the user file does not exist.
- Break out of the outer loop when `ttfx` exits non-zero, instead of relaunching.
- Move the keyboard poll so it runs even when `ttfx` is not alive.

### How we hit it

Not a hand-crafted repro. `~/.config/omarchy/branding` was a symlink into a
dotfiles repo; the repo dropped that directory because its contents were
byte-identical to Omarchy's own `logo.txt` and `icon.txt`, on the assumption
that Omarchy would fall back to its own defaults. It does not.

### System details

| | |
|---|---|
| Omarchy | 4.0.2-1 |
| ttfx | 0.3.2-1 |
| Kernel | 6.18.46-1-lts |
| CPU | Intel i7-4770HQ (Haswell) |
| GPU | Intel Iris Pro 5200 (Gen7.5, crocus) |
| Hardware | MacBookPro11,4 |

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

Not linked to any known issue, other report or change yet.

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 11:25 UTC