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.
omarchy restart shell cannot recover an IPC-wedged shell: no signal fallback
Reported by TempleZide · filed Sep 20, 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
What happened
omarchy restart shell cannot restart a shell whose IPC has stopped responding. It reports failure and leaves the old process running. This is the situation the command exists for, so the recovery path is unavailable exactly when it is needed.
I hit this after the Omarchy shell wedged on quickshell issue 1052, where a WirePlumber restart leaves Quickshell spinning on a dead PipeWire socket. The shell process stays alive and answers nothing. Running the documented recovery produced:
$ omarchy restart shell
Omarchy shell did not become ready after restart.The wedged process was untouched and still leaking. I had to kill -9 it by hand.
Why it fails
omarchy-restart-shell stops the old instance with a single IPC-based call:
# Each kill stops the oldest matching instance and only returns once it has
# fully exited, so the no-duplicate launch below can't race a dying shell.
while timeout 5 quickshell kill -p "$CONFIG_DIR" --any-display >/dev/null 2>&1; do :; donequickshell kill reaches the instance over its ipc.sock. When the shell does not service its event loop, that call fails, so the while loop exits immediately without having stopped anything. The launch that follows runs with -n, which refuses to start a duplicate, so no new shell appears either. The twenty ping attempts then time out and the command reports failure.
There is no signal fallback anywhere in the script.
A related problem in the same recovery path
The Quickshell instance log lives on tmpfs at /run/user/$UID/quickshell/by-id/<instance>/log.log. A spinning shell can fill that filesystem; mine reached 6,568,009,728 bytes and took /run/user/1000 to 100 percent.
Once the tmpfs is full, a newly launched shell cannot write its instance.lock. The file is created at zero bytes, qs list --all returns a corrupt record with process id 0 and a launch time in the year -4714, and qs ipc cannot match the live instance. So even after the old process is killed and a fresh shell starts, omarchy-shell shell ping still reports the shell is not running.
Recovery therefore needs the stale instance directories removed as well as the process killed. That is not obvious from the outside, and omarchy restart shell does not do it.
Steps to reproduce
Any wedge of the shell's event loop will do. The one I hit:
- Run the Omarchy shell normally.
systemctl --user restart wireplumber- Watch the shell begin spinning.
journalctl --user | grep 'Invalid socket'fills without pause and the resident set grows by roughly 47 MB per second. - Run
omarchy restart shell.
Expected: the old shell is stopped and a new one starts.
Actual: Omarchy shell did not become ready after restart. The old process keeps running and keeps growing.
A quicker synthetic version, if you would rather not involve PipeWire: kill -STOP the quickshell process, then run omarchy restart shell. The IPC kill cannot reach a stopped process either.
Suggested fix
After the IPC kill loop gives up, fall back to signals against the processes matching the config path: SIGTERM, wait briefly, then SIGKILL. Then remove the runtime directories of instances whose pid is gone, so a replacement can register even if the tmpfs was filled:
for d in "${XDG_RUNTIME_DIR:-/run/user/$UID}"/quickshell/by-pid/*; do
[[ -e $d ]] || continue
pid=$(basename "$d")
kill -0 "$pid" 2>/dev/null || { rm -rf "$(readlink -f "$d")" "$d"; }
done
find "${XDG_RUNTIME_DIR:-/run/user/$UID}"/quickshell/by-shell -xtype l -delete 2>/dev/nullThe existing comment in the script says the IPC kill "only returns once it has fully exited, so the no-duplicate launch below can't race a dying shell". That holds for a healthy shell. It does not hold when the kill never lands, and the script does not distinguish the two.
System details
- Omarchy 4.0.3-1
- Hyprland 0.56.2
- Kernel 7.2.3-arch1-3
- CPU: 12th Gen Intel Core i7-12700K
- GPU: NVIDIA GeForce RTX 3070 Ti (GA104)
- quickshell 0.3.1-1
- pipewire 1:1.6.8-1
- wireplumber 0.5.17-1
- qt6-base 6.11.2-3
Note: the contributing documentation I had references omarchy debug --no-sudo --print for diagnostics, but that command does not exist in 4.0.3-1 (Unknown Omarchy command: omarchy debug), so the details above were collected by hand.
View raw text
### What happened
`omarchy restart shell` cannot restart a shell whose IPC has stopped responding. It reports failure and leaves the old process running. This is the situation the command exists for, so the recovery path is unavailable exactly when it is needed.
I hit this after the Omarchy shell wedged on [quickshell issue 1052](https://github.com/quickshell-mirror/quickshell/issues/1052), where a WirePlumber restart leaves Quickshell spinning on a dead PipeWire socket. The shell process stays alive and answers nothing. Running the documented recovery produced:
```
$ omarchy restart shell
Omarchy shell did not become ready after restart.
```
The wedged process was untouched and still leaking. I had to `kill -9` it by hand.
### Why it fails
`omarchy-restart-shell` stops the old instance with a single IPC-based call:
```bash
# Each kill stops the oldest matching instance and only returns once it has
# fully exited, so the no-duplicate launch below can't race a dying shell.
while timeout 5 quickshell kill -p "$CONFIG_DIR" --any-display >/dev/null 2>&1; do :; done
```
`quickshell kill` reaches the instance over its `ipc.sock`. When the shell does not service its event loop, that call fails, so the `while` loop exits immediately without having stopped anything. The launch that follows runs with `-n`, which refuses to start a duplicate, so no new shell appears either. The twenty ping attempts then time out and the command reports failure.
There is no signal fallback anywhere in the script.
### A related problem in the same recovery path
The Quickshell instance log lives on tmpfs at `/run/user/$UID/quickshell/by-id/<instance>/log.log`. A spinning shell can fill that filesystem; mine reached 6,568,009,728 bytes and took `/run/user/1000` to 100 percent.
Once the tmpfs is full, a newly launched shell cannot write its `instance.lock`. The file is created at zero bytes, `qs list --all` returns a corrupt record with process id 0 and a launch time in the year -4714, and `qs ipc` cannot match the live instance. So even after the old process is killed and a fresh shell starts, `omarchy-shell shell ping` still reports the shell is not running.
Recovery therefore needs the stale instance directories removed as well as the process killed. That is not obvious from the outside, and `omarchy restart shell` does not do it.
### Steps to reproduce
Any wedge of the shell's event loop will do. The one I hit:
1. Run the Omarchy shell normally.
2. `systemctl --user restart wireplumber`
3. Watch the shell begin spinning. `journalctl --user | grep 'Invalid socket'` fills without pause and the resident set grows by roughly 47 MB per second.
4. Run `omarchy restart shell`.
Expected: the old shell is stopped and a new one starts.
Actual: `Omarchy shell did not become ready after restart.` The old process keeps running and keeps growing.
A quicker synthetic version, if you would rather not involve PipeWire: `kill -STOP` the quickshell process, then run `omarchy restart shell`. The IPC kill cannot reach a stopped process either.
### Suggested fix
After the IPC kill loop gives up, fall back to signals against the processes matching the config path: `SIGTERM`, wait briefly, then `SIGKILL`. Then remove the runtime directories of instances whose pid is gone, so a replacement can register even if the tmpfs was filled:
```bash
for d in "${XDG_RUNTIME_DIR:-/run/user/$UID}"/quickshell/by-pid/*; do
[[ -e $d ]] || continue
pid=$(basename "$d")
kill -0 "$pid" 2>/dev/null || { rm -rf "$(readlink -f "$d")" "$d"; }
done
find "${XDG_RUNTIME_DIR:-/run/user/$UID}"/quickshell/by-shell -xtype l -delete 2>/dev/null
```
The existing comment in the script says the IPC kill "only returns once it has fully exited, so the no-duplicate launch below can't race a dying shell". That holds for a healthy shell. It does not hold when the kill never lands, and the script does not distinguish the two.
### System details
- Omarchy 4.0.3-1
- Hyprland 0.56.2
- Kernel 7.2.3-arch1-3
- CPU: 12th Gen Intel Core i7-12700K
- GPU: NVIDIA GeForce RTX 3070 Ti (GA104)
- quickshell 0.3.1-1
- pipewire 1:1.6.8-1
- wireplumber 0.5.17-1
- qt6-base 6.11.2-3
Note: the contributing documentation I had references `omarchy debug --no-sudo --print` for diagnostics, but that command does not exist in 4.0.3-1 (`Unknown Omarchy command: omarchy debug`), so the details above were collected by hand.
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 16:43 UTC