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

Audio panel segfaults Quickshell when its cached sink/source list holds destroyed PwNodes

Reported by chava-byte · filed Oct 3, 2026Closed upstream on 2026-10-04 by the bot omarchybot; the closing comment says it was fixed by PR #7783. 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

Audio panel segfaults Quickshell when its cached sink/source list holds destroyed PwNodesrevision 1 · captured 2026-10-03 21:22 UTC via poll

What happened

Opening the audio panel crashed the Omarchy shell (SIGSEGV). Quickshell's crash handler restarted it in place.

Mechanism

shell/plugins/panels/audio/Panel.qml stores live PwNode objects in cachedAudioSinks / cachedAudioSources (lines 76-77, updated at 151-152), and audioSinks / audioSources fall back to those caches when rawAudioSinks / rawAudioSources is empty (lines 93-94).

When WirePlumber restarts (or a device goes away), those PwNodes are destroyed but stay in the cache as dead wrappers. If no replacement sink/source shows up, the live list is empty and the panel uses the stale cache. Opening the panel then runs:

onOpenedChanged → refreshDisplayAudioModels() → displayAudio* = listSnapshot(...) → Repeater { model: root.displayAudio* } rebuilds with a null QObject.

While the delegate incubates, the non-library Model.js is re-evaluated in the delegate context, and its top-level typeof module lookup goes through the delegate's model object. Qt's JS-array model adaptor (VDMListDelegateDataType, libQt6QmlModels) then calls a virtual method on the null QObject* (mov (%rax),%rdx with rax = 0).

This is the same class of bug that the bluetooth and network panels already work around by building primitives-only rows (see the comments on deviceRow() and wifiRow() in their Model.js).

In this session WirePlumber had been restarted 5 times, and afterwards the machine had no hardware sinks or sources at all (only the omarchy_speaker_tuning filter sink), so the fallback to the stale cache was taken.

Stack (abridged; Qt frames unsymbolized because Arch debuginfod has no debuginfo for these qt6-declarative build IDs)

#4  libQt6QmlModels.so.6 + 0xb3530        <- SIGSEGV, null QObject* virtual call
#5  libQt6QmlModels.so.6 + 0xb3f01
#6  libQt6Qml.so.6 + 0x3880a7
#7  QV4::QObjectWrapper::getQmlProperty
#8  QV4::QQmlContextWrapper::getPropertyAndBase
#10 QV4::Runtime::TypeofName::call
#13 QV4::Module::evaluate
#15 QQmlEnginePrivate::createInternalContext
#19 QQmlIncubatorPrivate::incubate
#22 QQuickRepeater::regenerate
#23 QQuickRepeater::setModel
#29 QQmlBinding::update
#36 QV4::Runtime::StoreNameSloppy::call   <- displayAudio* = ...
#40 QV4::Runtime::CallQmlContextPropertyLookup::call   <- refreshDisplayAudioModels()
#46 QQmlBoundSignalExpression::evaluate   <- onOpenedChanged
...
#106 QQuickMouseArea::mouseReleaseEvent

Note: I matched the QML side of the stack to the audio panel from the frame shape, the code, and the system state, not from a symbolized QML filename. Every step lines up with this path.

Expected

Opening the audio panel never crashes, even after audio devices disappear.

Steps to reproduce (expected)

  1. Have at least one hardware audio source/sink so the cache gets filled.
  2. Restart WirePlumber, or remove the device, so that no hardware source/sink comes back.
  3. Open the audio panel.

Suggested fix

Do what bluetooth and network already do: give the Repeaters primitive rows and resolve the PwNode on demand. Alternatively, filter cachedAudioSinks / cachedAudioSources against Pipewire.nodes.values before using them, or don't cache node objects at all.

System

  • Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy
  • Dell XPS 14 DA14260 (Intel Xe2 graphics)
  • quickshell 0.3.1-1, qt6-declarative 6.11.2-1, qt6-base 6.11.2-3
  • pipewire 1.6.8-1, wireplumber 0.5.17-1, hyprland 0.56.2-2

(omarchy debug isn't available in 4.0.4, so I collected these details by hand.)


Filed by Claude Opus 5.5 via Claude Code.

View raw text
## What happened

Opening the audio panel crashed the Omarchy shell (SIGSEGV). Quickshell's crash handler restarted it in place.

## Mechanism

`shell/plugins/panels/audio/Panel.qml` stores live `PwNode` objects in `cachedAudioSinks` / `cachedAudioSources` (lines 76-77, updated at 151-152), and `audioSinks` / `audioSources` fall back to those caches when `rawAudioSinks` / `rawAudioSources` is empty (lines 93-94).

When WirePlumber restarts (or a device goes away), those `PwNode`s are destroyed but stay in the cache as dead wrappers. If no replacement sink/source shows up, the live list is empty and the panel uses the stale cache. Opening the panel then runs:

`onOpenedChanged` → `refreshDisplayAudioModels()` → `displayAudio* = listSnapshot(...)` → `Repeater { model: root.displayAudio* }` rebuilds with a null QObject.

While the delegate incubates, the non-library `Model.js` is re-evaluated in the delegate context, and its top-level `typeof module` lookup goes through the delegate's model object. Qt's JS-array model adaptor (`VDMListDelegateDataType`, libQt6QmlModels) then calls a virtual method on the null QObject* (`mov (%rax),%rdx` with `rax = 0`).

This is the same class of bug that the bluetooth and network panels already work around by building primitives-only rows (see the comments on `deviceRow()` and `wifiRow()` in their `Model.js`).

In this session WirePlumber had been restarted 5 times, and afterwards the machine had no hardware sinks or sources at all (only the `omarchy_speaker_tuning` filter sink), so the fallback to the stale cache was taken.

## Stack (abridged; Qt frames unsymbolized because Arch debuginfod has no debuginfo for these qt6-declarative build IDs)

```
#4  libQt6QmlModels.so.6 + 0xb3530        <- SIGSEGV, null QObject* virtual call
#5  libQt6QmlModels.so.6 + 0xb3f01
#6  libQt6Qml.so.6 + 0x3880a7
#7  QV4::QObjectWrapper::getQmlProperty
#8  QV4::QQmlContextWrapper::getPropertyAndBase
#10 QV4::Runtime::TypeofName::call
#13 QV4::Module::evaluate
#15 QQmlEnginePrivate::createInternalContext
#19 QQmlIncubatorPrivate::incubate
#22 QQuickRepeater::regenerate
#23 QQuickRepeater::setModel
#29 QQmlBinding::update
#36 QV4::Runtime::StoreNameSloppy::call   <- displayAudio* = ...
#40 QV4::Runtime::CallQmlContextPropertyLookup::call   <- refreshDisplayAudioModels()
#46 QQmlBoundSignalExpression::evaluate   <- onOpenedChanged
...
#106 QQuickMouseArea::mouseReleaseEvent
```

Note: I matched the QML side of the stack to the audio panel from the frame shape, the code, and the system state, not from a symbolized QML filename. Every step lines up with this path.

## Expected

Opening the audio panel never crashes, even after audio devices disappear.

## Steps to reproduce (expected)

1. Have at least one hardware audio source/sink so the cache gets filled.
2. Restart WirePlumber, or remove the device, so that no hardware source/sink comes back.
3. Open the audio panel.

## Suggested fix

Do what bluetooth and network already do: give the Repeaters primitive rows and resolve the `PwNode` on demand. Alternatively, filter `cachedAudioSinks` / `cachedAudioSources` against `Pipewire.nodes.values` before using them, or don't cache node objects at all.

## System

- Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy
- Dell XPS 14 DA14260 (Intel Xe2 graphics)
- quickshell 0.3.1-1, qt6-declarative 6.11.2-1, qt6-base 6.11.2-3
- pipewire 1.6.8-1, wireplumber 0.5.17-1, hyprland 0.56.2-2

(`omarchy debug` isn't available in 4.0.4, so I collected these details by hand.)

---
Filed by Claude Opus 5.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.

No decision has been recorded yet.