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.
Shipped a2dp_source auto-connect makes a paired phone silence all audio
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 ships a WirePlumber rule that auto-connects the a2dp_source profile on every Bluetooth device. On any machine with a paired phone, that makes WirePlumber acquire an audio transport the phone never offered. BlueZ refuses with org.bluez.Error.NotAuthorized, and WirePlumber's Bluetooth node handler then dereferences a nil node. The Lua error aborts the event hook and the session manager stops linking audio streams for every device, not just the Bluetooth one.
The result is complete, silent audio loss with nothing in the obvious diagnostic surface pointing at Bluetooth. On my machine audio was dead for five hours before anyone connected it to a phone that had wandered into range at 05:25.
The shipped config
/usr/share/omarchy/config/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf:
## Auto-connect A2DP playback/capture profiles on Bluetooth devices.
## This helps speakers and receivers expose their audio profiles without
## requiring manual PipeWire/WirePlumber recovery.
monitor.bluez.rules = [
{
matches = [
{
device.name = "~bluez_card.*"
}
]
actions = {
update-props = {
bluez5.auto-connect = [ a2dp_sink a2dp_source ]
}
}
}
]Two things combine here:
- The match is
~bluez_card.*, so it applies to every Bluetooth device, not only audio output hardware. - The list includes
a2dp_source, which is the role for a device sending audio to this machine.
The stated intent in the comment is "speakers and receivers". Those are a2dp_sink. a2dp_source only brings in devices like phones, which is the case the comment does not describe.
WirePlumber ships no bluez5.auto-connect default of its own, so this behaviour comes entirely from Omarchy.
What it looks like when it fires
wireplumber[1952]: spa.bluez5: Acquire /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/fd0 returned error: org.bluez.Error.NotAuthorized
wireplumber[1952]: spa.bluez5: Failure in Bluetooth audio transport /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/fd0
wireplumber[1952]: mod.adapter: can't get format: Invalid argument
wireplumber[1952]: wp-node: failed to create node from factory 'adapter'
wireplumber[1952]: wplua: [string "create-node.lua"]:43: attempt to index a nil value (local 'node')From that point, every new stream lands unlinked:
$ pactl list sink-inputs
Sink Input #10444
Sink: 4294967295
application.name = "Zen"4294967295 is (uint32)-1, so the stream is attached to no sink. Meanwhile every service reports active, the default sink is unmuted at a normal volume, the ALSA mixer is open, and pactl info looks entirely healthy. Native PipeWire clients such as the Omarchy bar keep working, which makes the fault look application-specific rather than global.
systemctl --user restart pipewire-pulse does not help. Only restarting WirePlumber does.
The nil dereference is a WirePlumber bug
To be clear about ownership: the crash itself is upstream. scripts/monitors/bluez/create-node.lua does
42 local node = LocalNode("adapter", properties)
43 node:activate(Features.ALL, function (n, e)with no nil check on line 42. I am reporting that to WirePlumber separately.
The Omarchy side is that the shipped default is what reaches for a transport that was never offered, turning a refusal that should be a non-event into a session-wide audio outage. Removing a2dp_source avoids the trigger regardless of when upstream fixes the guard.
There is a second-order cost
Recovering from this means restarting WirePlumber. On Omarchy that runs straight into quickshell issue 1052: the shell begins spinning on the dead PipeWire socket and grows its resident set by about 47 MB per second. Mine reached 51 GB and filled /run/user/1000 to 100 percent before I caught it, which then broke Super+Space. I have filed the recovery-path half of that as a separate issue.
So one phone drifting into Bluetooth range can cost a user their audio, then their shell, then most of their RAM.
Suggested fix
Drop a2dp_source from the shipped list:
bluez5.auto-connect = [ a2dp_sink ]That preserves the documented intent, speakers and receivers auto-connecting, and removes the phone case entirely. If auto-connecting phone audio is wanted, it would be better as an opt-in, since acquiring a source transport unprompted is what BlueZ is refusing.
I have applied this locally and Bluetooth speakers still behave as before.
Steps to reproduce
- Pair a phone with an Omarchy machine and mark it trusted.
- Leave the stock
bluetooth-a2dp-autoconnect.confin place. - Let the phone come into range and connect.
- Watch
journalctl --user -u wireplumberfor theNotAuthorizedacquire and thecreate-node.luanil dereference. - Play audio from any PulseAudio client. It produces no sound, and
pactl list sink-inputsshowsSink: 4294967295.
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)
- wireplumber 0.5.17-1
- pipewire 1:1.6.8-1
- bluez 5.87-2
- Remote device: phone,
Icon: phone, advertising Audio Source (0000110a) and Headset AG (00001112)
View raw text
### What happened
Omarchy ships a WirePlumber rule that auto-connects the `a2dp_source` profile on every Bluetooth device. On any machine with a paired phone, that makes WirePlumber acquire an audio transport the phone never offered. BlueZ refuses with `org.bluez.Error.NotAuthorized`, and WirePlumber's Bluetooth node handler then dereferences a nil node. The Lua error aborts the event hook and **the session manager stops linking audio streams for every device**, not just the Bluetooth one.
The result is complete, silent audio loss with nothing in the obvious diagnostic surface pointing at Bluetooth. On my machine audio was dead for five hours before anyone connected it to a phone that had wandered into range at 05:25.
### The shipped config
`/usr/share/omarchy/config/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf`:
```
## Auto-connect A2DP playback/capture profiles on Bluetooth devices.
## This helps speakers and receivers expose their audio profiles without
## requiring manual PipeWire/WirePlumber recovery.
monitor.bluez.rules = [
{
matches = [
{
device.name = "~bluez_card.*"
}
]
actions = {
update-props = {
bluez5.auto-connect = [ a2dp_sink a2dp_source ]
}
}
}
]
```
Two things combine here:
1. The match is `~bluez_card.*`, so it applies to every Bluetooth device, not only audio output hardware.
2. The list includes `a2dp_source`, which is the role for a device **sending** audio to this machine.
The stated intent in the comment is "speakers and receivers". Those are `a2dp_sink`. `a2dp_source` only brings in devices like phones, which is the case the comment does not describe.
WirePlumber ships no `bluez5.auto-connect` default of its own, so this behaviour comes entirely from Omarchy.
### What it looks like when it fires
```
wireplumber[1952]: spa.bluez5: Acquire /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/fd0 returned error: org.bluez.Error.NotAuthorized
wireplumber[1952]: spa.bluez5: Failure in Bluetooth audio transport /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/fd0
wireplumber[1952]: mod.adapter: can't get format: Invalid argument
wireplumber[1952]: wp-node: failed to create node from factory 'adapter'
wireplumber[1952]: wplua: [string "create-node.lua"]:43: attempt to index a nil value (local 'node')
```
From that point, every new stream lands unlinked:
```
$ pactl list sink-inputs
Sink Input #10444
Sink: 4294967295
application.name = "Zen"
```
`4294967295` is `(uint32)-1`, so the stream is attached to no sink. Meanwhile every service reports active, the default sink is unmuted at a normal volume, the ALSA mixer is open, and `pactl info` looks entirely healthy. Native PipeWire clients such as the Omarchy bar keep working, which makes the fault look application-specific rather than global.
`systemctl --user restart pipewire-pulse` does not help. Only restarting WirePlumber does.
### The nil dereference is a WirePlumber bug
To be clear about ownership: the crash itself is upstream. `scripts/monitors/bluez/create-node.lua` does
```lua
42 local node = LocalNode("adapter", properties)
43 node:activate(Features.ALL, function (n, e)
```
with no nil check on line 42. I am reporting that to WirePlumber separately.
The Omarchy side is that the shipped default is what reaches for a transport that was never offered, turning a refusal that should be a non-event into a session-wide audio outage. Removing `a2dp_source` avoids the trigger regardless of when upstream fixes the guard.
### There is a second-order cost
Recovering from this means restarting WirePlumber. On Omarchy that runs straight into [quickshell issue 1052](https://github.com/quickshell-mirror/quickshell/issues/1052): the shell begins spinning on the dead PipeWire socket and grows its resident set by about 47 MB per second. Mine reached 51 GB and filled `/run/user/1000` to 100 percent before I caught it, which then broke `Super+Space`. I have filed the recovery-path half of that as a separate issue.
So one phone drifting into Bluetooth range can cost a user their audio, then their shell, then most of their RAM.
### Suggested fix
Drop `a2dp_source` from the shipped list:
```
bluez5.auto-connect = [ a2dp_sink ]
```
That preserves the documented intent, speakers and receivers auto-connecting, and removes the phone case entirely. If auto-connecting phone audio is wanted, it would be better as an opt-in, since acquiring a source transport unprompted is what BlueZ is refusing.
I have applied this locally and Bluetooth speakers still behave as before.
### Steps to reproduce
1. Pair a phone with an Omarchy machine and mark it trusted.
2. Leave the stock `bluetooth-a2dp-autoconnect.conf` in place.
3. Let the phone come into range and connect.
4. Watch `journalctl --user -u wireplumber` for the `NotAuthorized` acquire and the `create-node.lua` nil dereference.
5. Play audio from any PulseAudio client. It produces no sound, and `pactl list sink-inputs` shows `Sink: 4294967295`.
### 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)
- wireplumber 0.5.17-1
- pipewire 1:1.6.8-1
- bluez 5.87-2
- Remote device: phone, `Icon: phone`, advertising Audio Source (0000110a) and Headset AG (00001112)
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:44 UTC