You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every boot, my volume came up at 100%. I could turn it down during a session and it would stay put, but the next reboot it was back to blasting. Annoying enough to actually dig into.
8
+
Recently I noticed that my volume was at 100% after rebooting my desktop NixOS machine. My audio setup runs through a Schiit Fulla DAC over USB, nothing exotic, just a clean external DAC for headphones. I could turn the volume down during a session and it would stay put, but the next reboot it was back at full blast. It wasn't anything big but it was annoying enough to actually dig into.
9
9
10
10
## First hypothesis: stale state files
11
11
12
-
WirePlumber saves volume state to `~/.local/state/wireplumber/`. The obvious first suspect was a stale or corrupt state file. Checking the files, `restore-stream` hadn't been modified since June 2024 — a clear sign something was off. This turned out to be an old WirePlumber 0.4.x format file that 0.5.x no longer writes to, but was still reading from on boot. It had `volume=1.0` baked in.
12
+
WirePlumber saves volume state to `~/.local/state/wireplumber/`. The obvious first suspect was a stale or corrupt state file. Checking the files, `restore-stream` hadn't been modified since June 2024, a clear sign something was off. This turned out to be an old [WirePlumber 0.4.x format](https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/migration.html) file that 0.5.x no longer writes to, but was still reading from on boot. It had `volume=1.0` baked in.
13
13
14
-
Cleared the whole state directory and restarted WirePlumber. Set the volume, rebooted. Still 100%.
14
+
I cleared the whole state directory and restarted WirePlumber, set the volume to 50% and rebooted. Still came up at 100%.
15
15
16
16
## The pavucontrol clue
17
17
@@ -21,33 +21,33 @@ The most useful observation came from restarting WirePlumber manually:
21
21
systemctl --user restart wireplumber
22
22
```
23
23
24
-
Volume goes to 100%. Then opening `pavucontrol` — without touching any controls — silently drops it back to 50%.
24
+
This causes the volume to go 100% every time. Then opening `pavucontrol`, without touching any controls, causes it to silently drop back to 50%.
25
25
26
26
That told me the saved state was fine and the restore mechanism worked. It just wasn't triggering at init time. The volume was only being applied when a PulseAudio client connected.
27
27
28
28
## ALSA mixer failures
29
29
30
-
Looking at the WirePlumber journal with debug logging enabled, every restart showed the same errors: `spa.alsa: Unable to load mixer: Broken pipe`. With verbose output, all 12 errors were exclusively on `hw:3` — the Schiit Fulla DAC — across four retry cycles during ACP profile probing. The other three cards (HDMI, onboard, webcam) attached to their mixers without issue.
30
+
I enabled debug logging on WirePlumber and checked the journal. Every restart showed the same errors: `spa.alsa: Unable to load mixer: Broken pipe`. With verbose output, all 12 errors were exclusively on `hw:3`, the Schiit Fulla DAC, across four retry cycles during [ACP profile probing](https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/alsa.html). The other three cards (HDMI, onboard, webcam) attached to their mixers without issue.
31
31
32
-
This matters because WirePlumber uses the ALSA mixer handle to apply saved route volumes to the device. No mixer, no volume restore. The fallback is the hardware default — 1.0.
32
+
WirePlumber uses the ALSA mixer handle to apply [saved route volumes](https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/settings.html) to the device. No mixer, no volume restore. The fallback is the hardware default, 1.0.
33
33
34
-
The `pavucontrol` workaround worked because it goes through the PipeWire/PA compat layer rather than the ALSA mixer path, which successfully applied the saved state.
34
+
That also explained why the `pavucontrol` workaround worked. It goes through the PipeWire/PA compat layer rather than the ALSA mixer path, which successfully applied the saved state.
35
35
36
36
## Tracing it to the kernel
37
37
38
-
The kernel log had one relevant line, logged 9 seconds after the Schiit enumerated:
38
+
I checked the kernel log and found one relevant line, logged 9 seconds after the Schiit enumerated:
39
39
40
40
```
41
41
usb 1-5.4.3: 17:0: failed to get current value for ch 0 (-32)
42
42
```
43
43
44
-
Error `-32` is `EPIPE` — Broken Pipe at the USB level. The `snd_usb_audio` driver tried to send a `GET_CUR` control request to read the current volume from the device's feature unit, the USB endpoint stalled, and that permanently broke the ALSA mixer handle for the rest of the session.
44
+
Error `-32` is `EPIPE`, meaning Broken Pipe at the USB level. The [`snd_usb_audio`](https://docs.kernel.org/sound/alsa-configuration.html#module-snd-usb-audio) driver tried to send a `GET_CUR` control request to read the current volume from the device's feature unit. The USB endpoint stalled, and that permanently broke the ALSA mixer handle for the rest of the session.
45
45
46
-
Confirmed this by running `amixer -c 3 info` later in the session — still returning `Broken pipe`, long after boot. Not a timing issue, a permanent stall.
46
+
I confirmed this by running `amixer -c 3 info` later in the session. Still returning `Broken pipe`, long after boot. Not a timing issue, a permanent stall.
47
47
48
48
## The fix
49
49
50
-
`snd_usb_audio` has a module parameter for exactly this: `ignore_ctl_error=1`. It tells the driver to log and continue when a USB audio control request fails, rather than stalling the endpoint. With it set, the EPIPE is recorded but the mixer handle stays usable, WirePlumber can attach to it, and volume state is saved and restored correctly.
50
+
It turns out `snd_usb_audio` has a [module parameter](https://docs.kernel.org/sound/alsa-configuration.html#module-snd-usb-audio) for exactly this: `ignore_ctl_error=1`. It tells the driver to log and continue when a USB audio control request fails, rather than stalling the endpoint. With it set, the EPIPE is recorded but the mixer handle stays usable, WirePlumber can attach to it, and volume state is saved and restored correctly.
0 commit comments