Common Recovery Steps

Ordered from least to most disruptive. Work through these in order — stop when the problem is resolved.


Step 1: Check logs

Before touching any services, read the logs. Most problems are self-diagnosing.

# Last 50 lines from the mixer (most issues start here)
journalctl --user -u shiloh-mixer -n 50

# Full recent window
journalctl --user -u shiloh-mixer --since "5 min ago"

# All services together
journalctl --user -u shiloh-mixer -u shiloh-web-relay -u mixer_web --since "5 min ago"

Look for ERROR, WARN, REJECT, or panic lines. Cross-reference with the specific troubleshooting pages:

  • Mixer — JACK, config, permissions, xruns
  • Broadcaster — registration, rejects
  • Relay — no audio, crackling
  • Browser — WHEP, ICE, HTTPS
  • MIDI — device, channel, debounce

Step 2: Restart individual services

Restart only the failing service. The others continue running.

# Mixer only (reconnects broadcasters and relays automatically)
systemctl --user restart shiloh-mixer

# Web relay only (browser listener reconnects)
systemctl --user restart shiloh-web-relay

# Phoenix UI only (LiveView clients reconnect automatically)
systemctl --user restart mixer_web

After restarting the mixer, broadcasters and relays reconnect automatically (with exponential backoff, resetting after a 10-second stable session). Wait ~30 seconds before escalating.


Step 3: Restart JACK / PipeWire

If the mixer is crashing repeatedly or JACK ports are in a bad state (common after a PipeWire upgrade), restart the audio stack.

systemctl --user restart pipewire pipewire-pulse wireplumber
# Wait a few seconds for the graph to settle
systemctl --user restart shiloh-mixer

Check the graph is healthy after restart:

timeout 2 pw-top -b -n 3 | head -10

Look for at least one node with status R and non-zero QUANT/RATE.

VU meters still at 0 after restart?

This is the driverless-graph symptom. A hardware ALSA node must be clocking the graph. Add the WirePlumber keep-awake rule from OPERATIONS.md and restart WirePlumber.


Step 4: Rebuild the desktop-audio null-sink

If PulseAudio apps (Firefox, VLC) are stuck on “Unknown Output” or pavucontrol shows corked sink-inputs, the desktop-audio null-sink is broken.

# Verify the symptom
journalctl --user -u wireplumber --since "2 min ago" | grep "pending linkable"

# Fix: restart the PipeWire stack
systemctl --user restart pipewire pipewire-pulse wireplumber

# If still broken, clear WirePlumber's stream-restore state
cp ~/.local/state/wireplumber/restore-stream{,.bak.$(date +%s)}
: > ~/.local/state/wireplumber/restore-stream
systemctl --user restart wireplumber

Verify the sink is back:

pactl list sinks short | grep desktop-audio

Step 5: Full system restart sequence

Use this as a last resort when individual service restarts haven’t helped. Follow the exact order — dependency ordering matters.

# 1. Stop everything
systemctl --user stop mixer_web shiloh-web-relay shiloh-mixer

# 2. Restart the audio stack
systemctl --user restart wireplumber pipewire-pulse pipewire
sleep 3

# 3. Verify the graph is running
timeout 2 pw-top -b -n 3 | head -5

# 4. Start the mixer first (broadcasters and relays can't connect until it's up)
systemctl --user start shiloh-mixer
sleep 2

# 5. Check it's healthy
systemctl --user is-active shiloh-mixer
journalctl --user -u shiloh-mixer -n 10

# 6. Start the remaining services
systemctl --user start shiloh-web-relay mixer_web

After a full restart, broadcaster and relay clients reconnect within 30 seconds on their own backoff timers. The mixer UI LiveView reconnects automatically when mixer_web is back.


State file cleanup

If ~/mixer-state/ has stale JSON (e.g. from a crashed session), the mixer will overwrite files on next start. No manual cleanup is needed unless you want to reset to defaults:

# Reset persisted relay assignments (the only file actually written by broadcast.rs)
rm -f ~/mixer-state/relay-assignments.json
systemctl --user restart shiloh-mixer

Note: gains.json is not written by the mixer — gains are in-memory only.
Scenes are stored via DETS by mixer_web as scenes.dets, not as JSON by
shiloh-mixer. Meters and sessions JSON are written at runtime and not
persisted across restarts.