Summary
When MPD starts playing audio via a PipeWire output, the bar process (quickshell) immediately
crashes with SIGSEGV. The top panel disappears, brightness resets, and the shell restarts in
a loop. The issue is a reactive binding loop in modules/bar/Media.qml: playerStreamNode
reads from Audio.outputAppNodes, which PwObjectTracker then modifies, which re-fires the
binding, creating an infinite loop that segfaults quickshell.
Steps to reproduce
- Configure MPD with a PipeWire output (
type "pipewire" in mpd.conf)
- Start any MPD client and play a track
- The
mpd.PipeWire stream node appears in Audio.outputAppNodes
- Bar crashes immediately
Expected behavior
Bar stays alive. MPD playback is reflected in the media widget without any crash.
Logs
WARN scene: QML Media at @modules/bar/BarContent.qml[260:9]: Binding loop detected for property "playerStreamNode":
qs:@/qs/modules/bar/Media.qml:46:5
inir.service: Main process exited, code=dumped, status=11/SEGV
inir.service: Failed with result 'core-dump'.
Version
2.27.0(commit 0143406)
Compositor
26.04
Quickshell version
Quickshell 0.3.0 (revision , distributed by Arch Linux)
Qt version
qt6-base 6.11.1-1
Distro
Arch Linux
Panel family
ii
Relevant configuration
Additional notes
Root Cause
playerStreamNode in modules/bar/Media.qml is a reactive binding that reads
Audio.outputAppNodes. The PwObjectTracker directly below it is also bound to
playerStreamNode. When a new PipeWire node appears:
Audio.outputAppNodes changes → playerStreamNode re-evaluates
PwObjectTracker.objects changes → Audio.outputAppNodes changes again
- → infinite loop → SIGSEGV
// current (broken)
readonly property var playerStreamNode: {
...
return Audio.outputAppNodes.find(...) ?? null
}
PwObjectTracker { objects: root.playerStreamNode ? [root.playerStreamNode] : [] }
Suggested Fix
Convert playerStreamNode to an imperative property updated via Connections, with an
identity guard to prevent re-triggering the cycle:
property var playerStreamNode: null
function _findPlayerStreamNode() {
const p = root.activePlayer
if (!p) return null
const id = (p.identity ?? "").toLowerCase()
const entry = (p.desktopEntry ?? "").toLowerCase()
if (!id && !entry) return null
return Audio.outputAppNodes.find(n => {
const an = ((n.properties?.["application.name"] ?? n.name) ?? "").toLowerCase()
if (!an) return false
return (id && (an.includes(id) || id.includes(an)))
|| (entry && (an.includes(entry) || entry.includes(an)))
}) ?? null
}
Connections {
target: Audio
function onOutputAppNodesChanged() {
const found = root._findPlayerStreamNode()
if (found !== root.playerStreamNode)
root.playerStreamNode = found
}
}
PwObjectTracker { objects: root.playerStreamNode ? [root.playerStreamNode] : [] }
Also add playerStreamNode = _findPlayerStreamNode() inside onActivePlayerChanged to
handle player switches correctly.
I found out about that bug using claude and suggested fix is made by him so i should say about that.
Summary
When MPD starts playing audio via a PipeWire output, the bar process (quickshell) immediately
crashes with SIGSEGV. The top panel disappears, brightness resets, and the shell restarts in
a loop. The issue is a reactive binding loop in
modules/bar/Media.qml:playerStreamNodereads from
Audio.outputAppNodes, whichPwObjectTrackerthen modifies, which re-fires thebinding, creating an infinite loop that segfaults quickshell.
Steps to reproduce
type "pipewire"inmpd.conf)mpd.PipeWirestream node appears inAudio.outputAppNodesExpected behavior
Bar stays alive. MPD playback is reflected in the media widget without any crash.
Logs
Version
2.27.0(commit 0143406)
Compositor
26.04
Quickshell version
Quickshell 0.3.0 (revision , distributed by Arch Linux)
Qt version
qt6-base 6.11.1-1
Distro
Arch Linux
Panel family
ii
Relevant configuration
Additional notes
Root Cause
playerStreamNodeinmodules/bar/Media.qmlis a reactive binding that readsAudio.outputAppNodes. ThePwObjectTrackerdirectly below it is also bound toplayerStreamNode. When a new PipeWire node appears:Audio.outputAppNodeschanges →playerStreamNodere-evaluatesPwObjectTracker.objectschanges →Audio.outputAppNodeschanges againSuggested Fix
Convert playerStreamNode to an imperative property updated via Connections, with an
identity guard to prevent re-triggering the cycle:
Also add playerStreamNode = _findPlayerStreamNode() inside onActivePlayerChanged to
handle player switches correctly.
I found out about that bug using claude and suggested fix is made by him so i should say about that.