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

Launch OSD ("Launching <app>…") can get permanently stuck on screen

Reported by reyalicea · filed Aug 28, 2026Closed upstream on 2026-09-21 by bjarneo via an automated message, as a duplicate of #8242 (R-1462). GitHub close reason: completed.

Received and recorded verbatim. Nothing has been checked 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?
Not yet mapped to any requirement or component.
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. No decision has been recorded yet.

Original report

Launch OSD ("Launching <app>…") can get permanently stuck on screenrevision 1 · captured 2026-10-04 12:03 UTC via poll

Launch OSD ("Launching <app>…") can get permanently stuck on screen

What happened

After launching an app from the Omarchy menu, the persistent "Launching <app>…"
OSD overlay stayed on screen indefinitely. It never cleared on its own. The
omarchy-osd layer-shell surface remained up (visible card, duration: 0),
and omarchy-shell osd close was required to dismiss it manually.

What was expected

The launch OSD should always clear — either when the launched window appears,
or via the 15s timeout fallback — with no path that leaves it stranded.

Where it comes from

shell/services/AppLibrary.qml:

  • beginLaunchFeedback(name) starts launchDelay (2000 ms) and

launchTimeout (15000 ms).
- launchDelay.onTriggered sets launchOsdOpen = true and calls
`Quickshell.execDetached(["omarchy-shell", "osd", "show",
{icon, message, duration: 0}])`.
- closeLaunchFeedback() calls
Quickshell.execDetached(["omarchy-shell", "osd", "close"]).

shell/plugins/osd/Osd.qml: show() with duration <= 0 calls
hideTimer.stop() and leaves opened = true until an explicit close().
There is no self-heal / no max lifetime for a duration: 0 OSD.

Root cause (two compounding issues)

  1. Show/close race. show and close are dispatched as two *separate

detached* omarchy-shell processes. Each then makes its own qs ipc call
to the shell. Delivery order is decided by process scheduling, not by the
order the QML issued them. When a toplevel appears in roughly the same tick
the 2s launchDelay fires, closeLaunchFeedback() runs right after
launchDelay.onTriggered, so a close IPC can reach the shell before the
show IPC. Net result: opened ends up true with no timer, forever.

  1. Single-shot backstop with no retry. For a launch that never produces a

detectable new/active toplevel (app already running and merely refocused so
ToplevelManager.toplevels count and activeToplevel are unchanged, a
background/agent app, or a failed launch), the only thing that clears the
OSD is the one-shot launchTimeout → single closeLaunchFeedback(). If
that lone detached close fails to land (shell busy past the 2s
OMARCHY_SHELL_IPC_TIMEOUT, transient error), nothing ever retries and the
OSD is stranded.

Steps to reproduce

Exact timing is racy, but the shape:

  1. From the Omarchy menu, launch an app whose window takes ~2s to map, or an

app that is already open and just gets refocused without changing the
toplevel set.
2. Observe the "Launching …" OSD appear at the 2s mark.
3. Intermittently: the OSD does not clear after the window is up and does not
clear after 15s. It stays until omarchy-shell osd close /
omarchy restart shell.

Suggested fixes

  • Have Osd.show() enforce a hard maximum lifetime even for duration <= 0

(e.g. a separate long guard timer, ~30–60s), so no OSD can outlive its
purpose.
- Make the launch feedback converge on shell state rather than fire-and-forget:
issue show/close through a single serialized path (one IPC target method
that owns the launch-OSD lifecycle), or re-issue close on a short repeating
timer until osd state reports closed.
- In maybeFinishLaunchFeedback() / launchTimeout, always send close
unconditionally on timeout (drop the if (root.launchOsdOpen) guard for the
timeout path) and verify via the osd state IPC.

System details

  • Omarchy: 4.0.1-1
  • Kernel: 7.1.9-arch1-2
  • Hyprland: 0.56.2 (v0.56.2, efb5099)
  • Quickshell: 0.3.1 (Arch)
  • CPU: Intel Core i3-2350M
  • GPU: Intel 2nd Gen Core integrated graphics [8086:0116] (Sandy Bridge)
View raw text
# Launch OSD ("Launching <app>…") can get permanently stuck on screen

## What happened

After launching an app from the Omarchy menu, the persistent "Launching <app>…"
OSD overlay stayed on screen indefinitely. It never cleared on its own. The
`omarchy-osd` layer-shell surface remained up (visible card, `duration: 0`),
and `omarchy-shell osd close` was required to dismiss it manually.

## What was expected

The launch OSD should always clear — either when the launched window appears,
or via the 15s timeout fallback — with no path that leaves it stranded.

## Where it comes from

`shell/services/AppLibrary.qml`:

- `beginLaunchFeedback(name)` starts `launchDelay` (2000 ms) and
  `launchTimeout` (15000 ms).
- `launchDelay.onTriggered` sets `launchOsdOpen = true` and calls
  `Quickshell.execDetached(["omarchy-shell", "osd", "show",
  {icon, message, duration: 0}])`.
- `closeLaunchFeedback()` calls
  `Quickshell.execDetached(["omarchy-shell", "osd", "close"])`.

`shell/plugins/osd/Osd.qml`: `show()` with `duration <= 0` calls
`hideTimer.stop()` and leaves `opened = true` until an explicit `close()`.
There is no self-heal / no max lifetime for a `duration: 0` OSD.

## Root cause (two compounding issues)

1. **Show/close race.** `show` and `close` are dispatched as two *separate
   detached* `omarchy-shell` processes. Each then makes its own `qs ipc call`
   to the shell. Delivery order is decided by process scheduling, not by the
   order the QML issued them. When a toplevel appears in roughly the same tick
   the 2s `launchDelay` fires, `closeLaunchFeedback()` runs right after
   `launchDelay.onTriggered`, so a `close` IPC can reach the shell *before* the
   `show` IPC. Net result: `opened` ends up `true` with no timer, forever.

2. **Single-shot backstop with no retry.** For a launch that never produces a
   detectable new/active toplevel (app already running and merely refocused so
   `ToplevelManager.toplevels` count and `activeToplevel` are unchanged, a
   background/agent app, or a failed launch), the only thing that clears the
   OSD is the one-shot `launchTimeout` → single `closeLaunchFeedback()`. If
   that lone detached `close` fails to land (shell busy past the 2s
   `OMARCHY_SHELL_IPC_TIMEOUT`, transient error), nothing ever retries and the
   OSD is stranded.

## Steps to reproduce

Exact timing is racy, but the shape:

1. From the Omarchy menu, launch an app whose window takes ~2s to map, or an
   app that is already open and just gets refocused without changing the
   toplevel set.
2. Observe the "Launching …" OSD appear at the 2s mark.
3. Intermittently: the OSD does not clear after the window is up and does not
   clear after 15s. It stays until `omarchy-shell osd close` /
   `omarchy restart shell`.

## Suggested fixes

- Have `Osd.show()` enforce a hard maximum lifetime even for `duration <= 0`
  (e.g. a separate long guard timer, ~30–60s), so no OSD can outlive its
  purpose.
- Make the launch feedback converge on shell state rather than fire-and-forget:
  issue `show`/`close` through a single serialized path (one IPC target method
  that owns the launch-OSD lifecycle), or re-issue `close` on a short repeating
  timer until `osd state` reports `closed`.
- In `maybeFinishLaunchFeedback()` / `launchTimeout`, always send `close`
  unconditionally on timeout (drop the `if (root.launchOsdOpen)` guard for the
  timeout path) and verify via the `osd state` IPC.

## System details

- Omarchy: 4.0.1-1
- Kernel: 7.1.9-arch1-2
- Hyprland: 0.56.2 (v0.56.2, efb5099)
- Quickshell: 0.3.1 (Arch)
- CPU: Intel Core i3-2350M
- GPU: Intel 2nd Gen Core integrated graphics [8086:0116] (Sandy Bridge)

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.

No decision has been recorded yet.