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

Lock screen: one unreachable fprintd-list probe disables fingerprint for the rest of the lock

Reported by osborng · filed Aug 31, 2026Closed upstream on 2026-10-04 by commit 879d6583dace (closed by dhh). 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

Lock screen: one unreachable fprintd-list probe disables fingerprint for the rest of the lockrevision 1 · captured 2026-09-28 21:24 UTC via poll

What happens

fingerprintCheckProc in shell/plugins/lock/Service.qml decides whether fingerprint unlock is available by shelling out to fprintd-list, and collapses every non-matching result into "not configured":

command: ["bash", "-c", "if [[ -f /etc/pam.d/omarchy-lock-fingerprint ]] && command -v fprintd-list >/dev/null 2>&1 && fprintd-list \"$USER\" 2>/dev/null | grep -qi finger; then echo yes; else echo no; fi"]
...
onExited: {
  root.fingerprintConfigured = String(fingerprintCheckStdout.text || "").trim() === "yes"
  if (root.lockRequested && root.fingerprintConfigured) root.startFingerprint()
  else if (!root.fingerprintConfigured && fingerprintPam.active) fingerprintPam.abort()
}

But "the probe did not return an enrolled print" and "this user has no fingerprint configured" are not the same thing. The probe also fails when fprintd is momentarily unreachable — and then:

  • fingerprintConfigured goes false,
  • any in-flight fingerprint PAM session is aborted,
  • handleFingerprintFinished only re-arms fingerprintRetryTimer if (fingerprintConfigured), so the retry loop never runs again, and
  • nothing re-probes: refreshFingerprintStatus() is only called from beginLock().

So a single transient miss disables fingerprint unlock for the entire lock session, not for one attempt. The user gets no prompt and no indication why; the only way out is the password, and it stays broken until the next lock.

When it fires

The reliable trigger is the resume window. fprintd is D-Bus activated, and an activation request made while the daemon is stopping or while systemd is still working through the resume can simply fail:

Impossible to get devices: GDBus.Error:org.freedesktop.DBus.Error.NameHasNoOwner:
Could not activate remote peer 'net.reactivated.Fprint': activation request failed

fprintd-list prints that to stdout, it contains no enrolled-print line, so the probe answers "no" and fingerprint is switched off for the lock.

Observed here on 4.0.2 after a lid-close suspend (a local resume hook was installed, so this timeline is context rather than a stock repro — the deterministic repro below is stock):

14:49:37  resume; fprintd stopped
14:49:38  quickshell.service.pam.subprocess: Starting pam session ... "omarchy-lock-fingerprint"
          (no "Place your right index finger" prompt is ever relayed)
          (fprintd is never activated — no "Starting Fingerprint Authentication Daemon")
          (no further fingerprint PAM sessions at all)
14:49:51  unlocked via omarchy-lock-password

Steps to reproduce

Observed on stock 4.0.2 (omarchy.lock enabled, no local patches to the plugin).

  1. Enrol a fingerprint and confirm fingerprint unlock works on the lock screen.
  2. Make fprintd unreachable, the way the resume window does:

sudo systemctl mask fprintd && sudo systemctl stop fprintd
3. Lock the screen. No fingerprint prompt — expected so far.
4. Restore fprintd while still locked: sudo systemctl unmask fprintd
5. Wait, still locked. Fingerprint never returns.

Measured run: fprintd restored at 20:19:52.785, session stayed locked until 20:20:17.096 — 24.3 seconds locked with a healthy, reachable daemon, and in that window:

fingerprint prompts relayed:                 0
fprintd D-Bus activations:                   0
fingerprint PAM sessions started:            0

Unlock with the password, lock again, and fingerprint works immediately — the reader was fine the whole time. Two shorter runs (2.4s and 4.6s of healthy-but-locked time) behaved identically.

Step 5 is the bug: the reader is healthy and available, and the lock screen has permanently written it off for that session.

Expected

A probe that could not reach fprintd should be treated as "unknown", not as "not configured" — retried, not made terminal. Only a definitive answer from fprintd (or missing PAM config / missing fprintd-list) should disable fingerprint for the session.

Suggested fix

Make the probe three-state — yes / no / unknown — and on unknown keep the previous value and re-probe on a short bounded timer instead of tearing fingerprint down.

Two details that matter if anyone implements this:

  • Key the decision on `fprintd-list`'s output, not its exit status. On fprintd 1.94.5-2 it returns 0 and 1 inconsistently for identical input (seen on a user with no prints), so a flaky 1 would strand a perfectly good reader — a worse failure than the one being fixed. The unreachable case is recognisable from stdout (Could not activate remote peer ...), and the "definitely nothing enrolled" case from has no fingers enrolled.
  • Bound the retries (10 x 1s is plenty for the resume window) so a genuinely broken fprintd cannot spin for the life of the lock, and keep the interval clear of the 250ms fingerprintRetryTimer so this does not add a second tight loop.

I am running a patched clone of the plugin along these lines. With it, step 4 above recovers inside the same lock:

20:09:06.154  fingerprint-probe-unavailable: retry 1/10
20:09:07.694  fingerprint-probe-unavailable: retry 2/10
20:09:08.702  fingerprint-probe-unavailable: retry 3/10
20:09:09.712  fingerprint-probe-unavailable: retry 4/10
              Starting Fingerprint Authentication Daemon...
              Starting pam session ... "omarchy-lock-fingerprint"
              Relaying pam message: "Place your right index finger on the fingerprint reader"

Happy to open a PR if the approach looks right.

Related

  • #7176 (retry pacing) and #7229 (the suspend wedge) are adjacent but different: both are about attempts that reach a struggling reader. This one is upstream of that — the loop is switched off before any attempt is made.
  • PR #7158 changes this probe's match to grep -q ' - #', which correctly fixes a separate false-positive in the current expression (grep -qi finger also matches has no fingers enrolled and the device name Goodix MOC Fingerprint Sensor, so an unenrolled user currently reads as configured). That PR does not change the terminal fingerprintConfigured = ... === "yes" behaviour reported here; I have left a note on it.

Environment

Omarchy4.0.2-1
Quickshell0.3.1-1
fprintd1.94.5-2
libfprint1.94.100-1
Kernel7.1.9-arch1-2
ReaderGoodix 27c6:609c (Goodix MOC Fingerprint Sensor, press)
View raw text
### What happens

`fingerprintCheckProc` in `shell/plugins/lock/Service.qml` decides whether fingerprint unlock is available by shelling out to `fprintd-list`, and collapses every non-matching result into "not configured":

```qml
command: ["bash", "-c", "if [[ -f /etc/pam.d/omarchy-lock-fingerprint ]] && command -v fprintd-list >/dev/null 2>&1 && fprintd-list \"$USER\" 2>/dev/null | grep -qi finger; then echo yes; else echo no; fi"]
...
onExited: {
  root.fingerprintConfigured = String(fingerprintCheckStdout.text || "").trim() === "yes"
  if (root.lockRequested && root.fingerprintConfigured) root.startFingerprint()
  else if (!root.fingerprintConfigured && fingerprintPam.active) fingerprintPam.abort()
}
```

But "the probe did not return an enrolled print" and "this user has no fingerprint configured" are not the same thing. The probe also fails when fprintd is momentarily **unreachable** — and then:

- `fingerprintConfigured` goes false,
- any in-flight fingerprint PAM session is aborted,
- `handleFingerprintFinished` only re-arms `fingerprintRetryTimer` `if (fingerprintConfigured)`, so **the retry loop never runs again**, and
- nothing re-probes: `refreshFingerprintStatus()` is only called from `beginLock()`.

So a single transient miss disables fingerprint unlock **for the entire lock session**, not for one attempt. The user gets no prompt and no indication why; the only way out is the password, and it stays broken until the next lock.

### When it fires

The reliable trigger is the resume window. `fprintd` is D-Bus activated, and an activation request made while the daemon is stopping or while systemd is still working through the resume can simply fail:

```
Impossible to get devices: GDBus.Error:org.freedesktop.DBus.Error.NameHasNoOwner:
Could not activate remote peer 'net.reactivated.Fprint': activation request failed
```

`fprintd-list` prints that to stdout, it contains no enrolled-print line, so the probe answers "no" and fingerprint is switched off for the lock.

Observed here on 4.0.2 after a lid-close suspend (a local resume hook was installed, so this timeline is context rather than a stock repro — the deterministic repro below is stock):

```
14:49:37  resume; fprintd stopped
14:49:38  quickshell.service.pam.subprocess: Starting pam session ... "omarchy-lock-fingerprint"
          (no "Place your right index finger" prompt is ever relayed)
          (fprintd is never activated — no "Starting Fingerprint Authentication Daemon")
          (no further fingerprint PAM sessions at all)
14:49:51  unlocked via omarchy-lock-password
```

### Steps to reproduce

Observed on stock 4.0.2 (`omarchy.lock` enabled, no local patches to the plugin).

1. Enrol a fingerprint and confirm fingerprint unlock works on the lock screen.
2. Make fprintd unreachable, the way the resume window does:
   `sudo systemctl mask fprintd && sudo systemctl stop fprintd`
3. Lock the screen. No fingerprint prompt — expected so far.
4. Restore fprintd **while still locked**: `sudo systemctl unmask fprintd`
5. Wait, still locked. Fingerprint never returns.

Measured run: fprintd restored at 20:19:52.785, session stayed locked until 20:20:17.096 — **24.3 seconds locked with a healthy, reachable daemon**, and in that window:

```
fingerprint prompts relayed:                 0
fprintd D-Bus activations:                   0
fingerprint PAM sessions started:            0
```

Unlock with the password, lock again, and fingerprint works immediately — the reader was fine the whole time. Two shorter runs (2.4s and 4.6s of healthy-but-locked time) behaved identically.

Step 5 is the bug: the reader is healthy and available, and the lock screen has permanently written it off for that session.

### Expected

A probe that could not reach fprintd should be treated as "unknown", not as "not configured" — retried, not made terminal. Only a definitive answer from fprintd (or missing PAM config / missing `fprintd-list`) should disable fingerprint for the session.

### Suggested fix

Make the probe three-state — `yes` / `no` / `unknown` — and on `unknown` keep the previous value and re-probe on a short bounded timer instead of tearing fingerprint down.

Two details that matter if anyone implements this:

- **Key the decision on `fprintd-list`'s output, not its exit status.** On fprintd 1.94.5-2 it returns 0 and 1 inconsistently for identical input (seen on a user with no prints), so a flaky `1` would strand a perfectly good reader — a worse failure than the one being fixed. The unreachable case is recognisable from stdout (`Could not activate remote peer ...`), and the "definitely nothing enrolled" case from `has no fingers enrolled`.
- Bound the retries (10 x 1s is plenty for the resume window) so a genuinely broken fprintd cannot spin for the life of the lock, and keep the interval clear of the 250ms `fingerprintRetryTimer` so this does not add a second tight loop.

I am running a patched clone of the plugin along these lines. With it, step 4 above recovers inside the same lock:

```
20:09:06.154  fingerprint-probe-unavailable: retry 1/10
20:09:07.694  fingerprint-probe-unavailable: retry 2/10
20:09:08.702  fingerprint-probe-unavailable: retry 3/10
20:09:09.712  fingerprint-probe-unavailable: retry 4/10
              Starting Fingerprint Authentication Daemon...
              Starting pam session ... "omarchy-lock-fingerprint"
              Relaying pam message: "Place your right index finger on the fingerprint reader"
```

Happy to open a PR if the approach looks right.

### Related

- #7176 (retry pacing) and #7229 (the suspend wedge) are adjacent but different: both are about attempts that *reach* a struggling reader. This one is upstream of that — the loop is switched off before any attempt is made.
- PR #7158 changes this probe's match to `grep -q ' - #'`, which correctly fixes a separate false-positive in the current expression (`grep -qi finger` also matches `has no fingers enrolled` and the device name `Goodix MOC Fingerprint Sensor`, so an unenrolled user currently reads as configured). That PR does not change the terminal `fingerprintConfigured = ... === "yes"` behaviour reported here; I have left a note on it.

### Environment

| | |
|---|---|
| Omarchy | 4.0.2-1 |
| Quickshell | 0.3.1-1 |
| fprintd | 1.94.5-2 |
| libfprint | 1.94.100-1 |
| Kernel | 7.1.9-arch1-2 |
| Reader | Goodix 27c6:609c (`Goodix MOC Fingerprint Sensor`, press) |

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.