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

snapper.sh installs a btrfs snapper config on non-btrfs roots, so snapper-cleanup fails daily

Reported by tpatzelt · filed Aug 10, 2026 · open at source

Decided: Out of scope — decided by agents · verified 4 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

snapper.sh installs a btrfs snapper config on non-btrfs roots, so snapper-cleanup fails dailyrevision 1 · captured 2026-09-28 21:29 UTC via poll

System details

Intel i5-10210U, Intel UHD Graphics (CometLake-U GT2), Omarchy 4.0.0 (bb8d2f2), root on ext4

What's wrong?

On a machine whose root filesystem isn't btrfs, install/config/snapper.sh writes a snapper config that claims it is, and then enables the cleanup timer for it. The result is snapper-cleanup.service failing every single day forever.

My root is a plain ext4 partition — no btrfs, no LVM thin pool, no /.snapshots:

$ findmnt / -o TARGET,SOURCE,FSTYPE
TARGET SOURCE         FSTYPE
/      /dev/nvme0n1p3 ext4

But /etc/snapper/configs/root exists and says otherwise:

# Omarchy snapshots root only for pre-update recovery — kept to 5, no timeline
SUBVOLUME="/"
FSTYPE="btrfs"

So the daily timer fires and dies:

× snapper-cleanup.service - Daily Cleanup of Snapper Snapshots
     Active: failed (Result: exit-code)

systemd-helper[1148211]: Running number cleanup for 'root'.
systemd-helper[1148211]: IO Error (query default id failed, subvolume is not a btrfs subvolume).
systemd-helper[1148211]: number cleanup for 'root' failed.
systemd[1]: snapper-cleanup.service: Failed with result 'exit-code'.

The cause is that the config gets installed unconditionally. In install/config/snapper.sh, create-config correctly fails on ext4 and is swallowed, but the very next line copies the template over the top anyway, and then the timer is enabled regardless:

snapper --no-dbus -c root create-config / >/dev/null 2>&1 || snapper -c root create-config / >/dev/null
fi

install -m 0644 "$template" "$SNAPPER_CONFIG_PATH"   # runs even when create-config failed
...
systemctl enable --now snapper-cleanup.timer limine-snapper-sync.service >/dev/null 2>&1 || true

Since default/snapper/root hardcodes FSTYPE="btrfs", every non-btrfs install ends up with a config describing a filesystem it doesn't have. There is no btrfs check anywhere in the script.

There's a second consequence beyond the noisy failed unit. omarchy-snapshot create runs under set -e and loops over whatever snapper list-configs returns:

mapfile -t CONFIGS < <(sudo snapper --csvout list-configs | awk -F, 'NR>1 {print $1}')

for config in "${CONFIGS[@]}"; do
  sudo snapper -c "$config" create -c number -d "$DESC"

The phantom root config is in that list, snapper create fails on it, and the script exits non-zero — which is the pre-update snapshot step.

limine-snapper-sync.service also gets enabled on these machines and just announces the same thing on every boot:

limine-snapper-watcher[629]: WARNING: Root filesystem is not Btrfs. limine-snapper-watcher stopped.

Suggested fix: guard the whole thing on the actual filesystem, e.g. bail early if findmnt -no FSTYPE / isn't btrfs, and don't enable the timer or limine-snapper-sync.service when there's nothing for them to do.

The local workaround is to remove /etc/snapper/configs/root and disable snapper-cleanup.timer and limine-snapper-sync.service. Nothing is lost by doing so, since no snapshot was ever created on these machines in the first place.

View raw text
### System details

Intel i5-10210U, Intel UHD Graphics (CometLake-U GT2), Omarchy 4.0.0 (bb8d2f2), root on ext4

### What's wrong?

On a machine whose root filesystem isn't btrfs, `install/config/snapper.sh` writes a snapper config that claims it is, and then enables the cleanup timer for it. The result is `snapper-cleanup.service` failing every single day forever.

My root is a plain ext4 partition — no btrfs, no LVM thin pool, no `/.snapshots`:

```
$ findmnt / -o TARGET,SOURCE,FSTYPE
TARGET SOURCE         FSTYPE
/      /dev/nvme0n1p3 ext4
```

But `/etc/snapper/configs/root` exists and says otherwise:

```
# Omarchy snapshots root only for pre-update recovery — kept to 5, no timeline
SUBVOLUME="/"
FSTYPE="btrfs"
```

So the daily timer fires and dies:

```
× snapper-cleanup.service - Daily Cleanup of Snapper Snapshots
     Active: failed (Result: exit-code)

systemd-helper[1148211]: Running number cleanup for 'root'.
systemd-helper[1148211]: IO Error (query default id failed, subvolume is not a btrfs subvolume).
systemd-helper[1148211]: number cleanup for 'root' failed.
systemd[1]: snapper-cleanup.service: Failed with result 'exit-code'.
```

The cause is that the config gets installed unconditionally. In `install/config/snapper.sh`, `create-config` correctly fails on ext4 and is swallowed, but the very next line copies the template over the top anyway, and then the timer is enabled regardless:

```bash
snapper --no-dbus -c root create-config / >/dev/null 2>&1 || snapper -c root create-config / >/dev/null
fi

install -m 0644 "$template" "$SNAPPER_CONFIG_PATH"   # runs even when create-config failed
...
systemctl enable --now snapper-cleanup.timer limine-snapper-sync.service >/dev/null 2>&1 || true
```

Since `default/snapper/root` hardcodes `FSTYPE="btrfs"`, every non-btrfs install ends up with a config describing a filesystem it doesn't have. There is no btrfs check anywhere in the script.

There's a second consequence beyond the noisy failed unit. `omarchy-snapshot create` runs under `set -e` and loops over whatever `snapper list-configs` returns:

```bash
mapfile -t CONFIGS < <(sudo snapper --csvout list-configs | awk -F, 'NR>1 {print $1}')

for config in "${CONFIGS[@]}"; do
  sudo snapper -c "$config" create -c number -d "$DESC"
```

The phantom `root` config is in that list, `snapper create` fails on it, and the script exits non-zero — which is the pre-update snapshot step.

`limine-snapper-sync.service` also gets enabled on these machines and just announces the same thing on every boot:

```
limine-snapper-watcher[629]: WARNING: Root filesystem is not Btrfs. limine-snapper-watcher stopped.
```

Suggested fix: guard the whole thing on the actual filesystem, e.g. bail early if `findmnt -no FSTYPE /` isn't `btrfs`, and don't enable the timer or `limine-snapper-sync.service` when there's nothing for them to do.

The local workaround is to remove `/etc/snapper/configs/root` and disable `snapper-cleanup.timer` and `limine-snapper-sync.service`. Nothing is lost by doing so, since no snapshot was ever created on these machines in the first place.

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 04:01 UTC