Troubleshooting & Diagnostics
Diagnosing display capture, PipeWire audio routing, controller mappings, and GPU encoding issues.
Quick Diagnostic Checklist#
When encountering streaming issues, follow this verification flow:
- Check Logs: Open https://localhost:47990/logs or run
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -n 100. - Check Port Bindings: Verify UDP 47998-48010 and TCP 47984/47990 are listening (
ss -tulwn | grep -E "4798|4799|4800|4801"). - Inspect Subsystem Errors: Query
/api/errorsto check if encoder or capture counters are incrementing.

Capture, audio, and network diagnostics#
Pick the symptom tab that matches the Moonlight overlay or host logs.
Confirm capture backend (kms, portal, x11). KMS needs cap_sys_admin. NVIDIA needs nvidia_drm.modeset=1. KWin overlays can break KMS on Plasma 6.5+ (KWIN_USE_OVERLAYS=0).
getcap "$(command -v sunshine)"
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -n 80 --no-pagerPipeWire & Audio Sink Diagnostics#
SolarFlare connects to PipeWire directly.
- If no audio is received on client: Open
pavucontrolorqpwgraphwhile streaming. Verify that the SolarFlare capture stream is linked to your default audio sink monitor. - If audio crackles: Increase
pipewire_latency_msfrom1or4up to8or12ms.
Black screen by capture backend#
SolarFlare has six capture paths and each fails differently. Identify yours first, then apply the matching fix:
getcap "$(command -v sunshine)"
ls -l /dev/dri/card* /dev/dri/render*
echo "$XDG_SESSION_TYPE $WAYLAND_DISPLAY $DISPLAY"
cat /sys/module/nvidia_drm/parameters/modeset
grep -E "^capture =|^adapter_name|^output_name" ~/.config/sunshine/sunshine.conf
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -n 80 --no-pager | grep -iE "kms|capture|portal|wayland|x11|hermes|Probably not permitted"| Backend | Failure signature | Fix |
|---|---|---|
| KMS | Probably not permitted on /dev/dri | setcap cap_sys_admin,cap_sys_nice+p; user in video group; re-login |
| KMS NVIDIA | No modes / modeset N | nvidia_drm.modeset=1 kernel param, reboot |
| Wayland | Missing output metadata | skip_wayland_correlation = true only as last resort (breaks absolute mouse) |
| Portal | portal session closed | Check xdg-desktop-portal running; re-accept the share dialog |
| X11 | No displays / wrong screen | DISPLAY=:0 set; xrandr --listmonitors; VIRTUAL1 or headless_mode |
| Hermes-KMS | Module absent | Install headers + DKMS module; look for HERMES-1 source |
| Plasma 6.5+ | Flicker with overlays | KWIN_USE_OVERLAYS=0 |
Encoder failures by vendor#
When logs say no working encoder was found, check the vendor stack directly. HDR fallbacks (hevc/av1 modes 3+) silently downgrade when the chain is not 10-bit end to end.
vainfo; vainfo --display drm --device /dev/dri/renderD128
nvidia-smi --query-gpu=name,driver_version --format=csv
lspci | grep -E 'VGA|3D'; ls -l /dev/dri/render*
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -n 200 --no-pager | grep -iE "encoder|nvenc|vaapi|qsv|vulkan|sw_preset|not supported"Pairing & discovery failures#
PIN failures are usually time skew, firewall asymmetry, or origin policy — not the PIN itself. Host and client clocks must agree within ~30 s. mDNS needs one broadcast domain with no AP isolation; otherwise add the host by LAN IP.
timedatectl status
ss -tlnp | grep -E '47984|47990|48010'; ss -ulnp | grep -E '47998|48000'
curl -sk https://127.0.0.1:47990/api/health | jq .
grep -E "trusted_subnets|trusted_subnet_auto_pairing|origin_web_ui_allowed|const_pin|upnp|^port =" ~/.config/sunshine/sunshine.conf
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -n 100 --no-pager | grep -iE "pair|pin|cert|crypto|origin"Web UI 401 / 403 / CSRF errors#
401 means no or bad credentials; 403 means scope, origin policy, or CSRF. Browser cross-origin POSTs need X-CSRF-Token from GET /api/csrf-token (1 h TTL); curl without Origin/Referer is exempt. Ten login failures per IP per 30 s triggers 429. Forgotten passwords reset with sunshine --creds followed by a service restart.
curl -vk https://localhost:47990/api/config -w "%{http_code}"
CSRF=$(curl -sk -u "$USER:$PASS" https://localhost:47990/api/csrf-token | jq -r .csrf_token)
grep -E "csrf_allowed_origins|origin_web_ui_allowed|api_tokens" ~/.config/sunshine/sunshine.conf
sunshine --creds "$USER" "$PASS"; systemctl --user restart app-dev.lizardbyte.app.Sunshine.serviceLogs, error counters & telemetry#
Error counters (encoder, capture, network, session, process, config, crypto, unknown, total) are monotonic since start — diff the total over 60 s and want zero deltas. min_log_level accepts verbose, debug, info, warning, error, fatal. Never run two instances: stop the user service before a foreground repro.
curl -sk -H "Authorization: Bearer $TOK" https://localhost:47990/api/errors | jq .
curl -sk -H "Authorization: Bearer $TOK" https://localhost:47990/api/logs | tail -100
SUNSHINE_LOG_JSON=1 systemctl --user restart app-dev.lizardbyte.app.Sunshine.service
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service -p err -b -n 100 --no-pagerSelf-update failures#
Updater phases: idle, checking, downloading, verifying, ready, waiting_idle, applying, restarting, error, unsupported (non-Linux). A non-empty session list blocks apply unless when_idle is false. SHA256SUMS mismatches, missing pkexec/helper, and lost setcap after binary swap are the common causes.
curl -sk -H "Authorization: Bearer $TOK" https://localhost:47990/api/update | jq '{phase,message,percent,busy,can_apply,latest_tag,outdated}'
curl -sk -H "Authorization: Bearer $TOK" https://localhost:47990/api/sessions | jq .
journalctl --user -u app-dev.lizardbyte.app.Sunshine.service --no-pager | grep -iE "update|staging|SHA-256|tarball|setcap|pkexec|helper"