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.
Bluetooth panel: unbounded StartDiscovery retry wedges the controller and kills LE background scanning
Reported by bukson · filed Sep 4, 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 happens
Opening the Bluetooth panel on a controller that is slow to confirm StartDiscovery
puts the shell into an unbounded 1 Hz command storm. On a Realtek RTL8761BU the
controller wedges within ~2 seconds, the kernel force-resets the USB device, and
LE background scanning dies. Any already-paired BLE device — in my case a
Logitech MX Vertical mouse — then stops auto-reconnecting, because the adapter is
no longer listening for its advertisements.
The panel does not have to be interacted with. Opening it is enough.
Mechanism
shell/plugins/panels/bluetooth/Panel.qml (4.0.2-1, unchanged since at least 4.0.0):
Timer {
id: discoveryRetry
interval: 1000
repeat: true
triggeredOnStart: true
running: root.opened && root.adapter !== null && root.adapter.enabled && !root.adapter.discovering
onTriggered: {
root.owesDiscoveryStop = true
root.adapter.discovering = true
}
}The running guard clears only when BlueZ confirms the start by flippingadapter.discovering. When a controller accepts the D-Bus call but never
completes the underlying HCI 0x200c (LE Set Scan Enable), that confirmation
never arrives, so the guard never clears and the timer re-issues StartDiscovery
every second for as long as the panel stays open.
The chain:
panel opened
-> StartDiscovery at 1 Hz ("Operation already in progress" x N)
-> hci0: command 0x200c tx timeout
-> btusb: "Resetting usb device."
-> "start background scanning failed: -110"
-> LE background scanning is dead
-> paired BLE devices no longer auto-reconnectEvidence
Captured on 4.0.2-1, 3 Sep 2026, opening the panel and leaving it alone:
12:22:34 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 kernel: Bluetooth: hci0: command 0x200c tx timeout
12:22:35 kernel: Bluetooth: hci0: Opcode 0x200c failed: -110
12:22:35 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 kernel: Bluetooth: hci0: Resetting usb device.
12:22:36 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:37 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:37 kernel: Bluetooth: hci0: Opcode 0x200c failed: -110
12:22:37 kernel: Bluetooth: hci0: start background scanning failed: -110The reset re-enumerates the adapter, which is why the index walks hci0 -> hci1.
Three such resets over that boot, and it has recurred on every boot since. On
earlier boots the mouse took ~17s to come back each time; with the panel left
closed it reconnects normally and the journal stays clean.
Note the contrast a few lines further down the same file. discoveryStop bounds
itself, with a comment naming this exact hazard:
// Attempts are bounded so a session some other BlueZ client keeps up cannot
// draw StopDiscovery fire forever.
attempts += 1
if (attempts > 3) { root.owesDiscoveryStop = false; return }The stop path was hardened against an unresponsive peer; the start path was not.
Suggested fix
Give discoveryRetry the same treatment as its sibling: an attempts counter, a
cap, and a latch in the running guard, reset in the opened branch ofonOpenedChanged so each panel visit gets a fresh budget. A controller that has
not confirmed after a few tries is not going to, and continuing to pushStartDiscovery at it is what causes the damage.
Reproduction
- Use a controller that is slow to confirm
StartDiscovery(RTL8761B/BU is a
common one — TP-Link UB500 and its many relabels).
2. Pair a BLE device that relies on background scanning to reconnect.
3. Open the Bluetooth panel and leave it open ~30 seconds. Do not click anything.
4. journalctl -f | grep -E '0x200c|Resetting usb|background scanning'
Expected: a bounded number of start attempts, then the panel gives up.
Actual: one Failed to start discovery per second, then 0x200c tx timeout,
a USB reset, and start background scanning failed: -110.
Not a regression in Omarchy's code
Worth stating plainly, since the timing invites the wrong conclusion. The symptom
started for me around 25 Aug, but the Bluetooth panel files are byte-identical
between 4.0.0-1 and 4.0.1-1 — I diffed the packages. The retry loop is a
long-standing latent bug; something else in that update window (linux
7.1.8.arch1-3 -> 7.1.9.arch1-2, or quickshell 0.3.0-2 -> 0.3.1-1) appears to
have changed how much of this the controller tolerates. I have not bisected that,
and it does not really matter for this report: a controller that never confirmsStartDiscovery should not draw unbounded fire regardless of which layer got
less forgiving.
Possibly related, but distinct
- #8614 — panel discovery competing with BR/EDR paging (
br-connection-page-timeout) - #7384 — protecting active A2DP audio from panel discovery
Both are about discovery running. This one is about the retry loop's failure to
ever stop trying to start it.
System
| Omarchy | 4.0.2-1 |
| quickshell | 0.3.1-1 |
| Kernel | 7.1.9-arch1-2 |
| BlueZ | 5.87-2 |
| Hyprland | 0.56.2 |
| Board | MSI MAG B460 TOMAHAWK (MS-7C81) |
| Adapter | Realtek RTL8761BU, USB 2550:8761, fw 0xdfc6d922 |
| Affected device | Logitech MX Vertical (BLE, random address) |
Filed by Claude Opus 5 via Claude Code.
View raw text
## What happens
Opening the Bluetooth panel on a controller that is slow to confirm `StartDiscovery`
puts the shell into an unbounded 1 Hz command storm. On a Realtek RTL8761BU the
controller wedges within ~2 seconds, the kernel force-resets the USB device, and
**LE background scanning dies**. Any already-paired BLE device — in my case a
Logitech MX Vertical mouse — then stops auto-reconnecting, because the adapter is
no longer listening for its advertisements.
The panel does not have to be interacted with. Opening it is enough.
## Mechanism
`shell/plugins/panels/bluetooth/Panel.qml` (4.0.2-1, unchanged since at least 4.0.0):
```qml
Timer {
id: discoveryRetry
interval: 1000
repeat: true
triggeredOnStart: true
running: root.opened && root.adapter !== null && root.adapter.enabled && !root.adapter.discovering
onTriggered: {
root.owesDiscoveryStop = true
root.adapter.discovering = true
}
}
```
The `running` guard clears only when BlueZ **confirms** the start by flipping
`adapter.discovering`. When a controller accepts the D-Bus call but never
completes the underlying HCI `0x200c` (LE Set Scan Enable), that confirmation
never arrives, so the guard never clears and the timer re-issues `StartDiscovery`
every second for as long as the panel stays open.
The chain:
```
panel opened
-> StartDiscovery at 1 Hz ("Operation already in progress" x N)
-> hci0: command 0x200c tx timeout
-> btusb: "Resetting usb device."
-> "start background scanning failed: -110"
-> LE background scanning is dead
-> paired BLE devices no longer auto-reconnect
```
## Evidence
Captured on 4.0.2-1, 3 Sep 2026, opening the panel and leaving it alone:
```
12:22:34 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 kernel: Bluetooth: hci0: command 0x200c tx timeout
12:22:35 kernel: Bluetooth: hci0: Opcode 0x200c failed: -110
12:22:35 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:35 kernel: Bluetooth: hci0: Resetting usb device.
12:22:36 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:37 omarchy-shell[1351]: WARN quickshell.bluetooth.adapter: Failed to start discovery on adapter ... "Operation already in progress"
12:22:37 kernel: Bluetooth: hci0: Opcode 0x200c failed: -110
12:22:37 kernel: Bluetooth: hci0: start background scanning failed: -110
```
The reset re-enumerates the adapter, which is why the index walks `hci0 -> hci1`.
Three such resets over that boot, and it has recurred on every boot since. On
earlier boots the mouse took ~17s to come back each time; with the panel left
closed it reconnects normally and the journal stays clean.
Note the contrast a few lines further down the same file. `discoveryStop` bounds
itself, with a comment naming this exact hazard:
```qml
// Attempts are bounded so a session some other BlueZ client keeps up cannot
// draw StopDiscovery fire forever.
attempts += 1
if (attempts > 3) { root.owesDiscoveryStop = false; return }
```
The stop path was hardened against an unresponsive peer; the start path was not.
## Suggested fix
Give `discoveryRetry` the same treatment as its sibling: an `attempts` counter, a
cap, and a latch in the `running` guard, reset in the `opened` branch of
`onOpenedChanged` so each panel visit gets a fresh budget. A controller that has
not confirmed after a few tries is not going to, and continuing to push
`StartDiscovery` at it is what causes the damage.
## Reproduction
1. Use a controller that is slow to confirm `StartDiscovery` (RTL8761B/BU is a
common one — TP-Link UB500 and its many relabels).
2. Pair a BLE device that relies on background scanning to reconnect.
3. Open the Bluetooth panel and leave it open ~30 seconds. Do not click anything.
4. `journalctl -f | grep -E '0x200c|Resetting usb|background scanning'`
Expected: a bounded number of start attempts, then the panel gives up.
Actual: one `Failed to start discovery` per second, then `0x200c tx timeout`,
a USB reset, and `start background scanning failed: -110`.
## Not a regression in Omarchy's code
Worth stating plainly, since the timing invites the wrong conclusion. The symptom
started for me around 25 Aug, but the Bluetooth panel files are byte-identical
between 4.0.0-1 and 4.0.1-1 — I diffed the packages. The retry loop is a
long-standing latent bug; something else in that update window (`linux`
7.1.8.arch1-3 -> 7.1.9.arch1-2, or `quickshell` 0.3.0-2 -> 0.3.1-1) appears to
have changed how much of this the controller tolerates. I have not bisected that,
and it does not really matter for this report: a controller that never confirms
`StartDiscovery` should not draw unbounded fire regardless of which layer got
less forgiving.
## Possibly related, but distinct
- #8614 — panel discovery competing with BR/EDR paging (`br-connection-page-timeout`)
- #7384 — protecting active A2DP audio from panel discovery
Both are about discovery *running*. This one is about the retry loop's failure to
ever stop trying to start it.
## System
| | |
|---|---|
| Omarchy | 4.0.2-1 |
| quickshell | 0.3.1-1 |
| Kernel | 7.1.9-arch1-2 |
| BlueZ | 5.87-2 |
| Hyprland | 0.56.2 |
| Board | MSI MAG B460 TOMAHAWK (MS-7C81) |
| Adapter | Realtek RTL8761BU, USB `2550:8761`, fw `0xdfc6d922` |
| Affected device | Logitech MX Vertical (BLE, random address) |
---
Filed by Claude Opus 5 via Claude Code.
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 10:45 UTC