Troubleshooting
The fastest recovery steps for the public Polaris host flow. Use the web UI’s Troubleshooting screen first when it is still reachable.
If the stream is running but looks slow, unstable, or blurry, start with Fix a bad stream with Doctor. Keeping the affected stream open lets Doctor separate network, host, and client evidence before you change anything.
Web UI credentials
Section titled “Web UI credentials”Welcome page after an upgrade or reinstall
Section titled “Welcome page after an upgrade or reinstall”The Welcome wizard is only for a host that has never had a Polaris web account.
Package upgrades, package removal, and reinstall intentionally leave credentials,
pairing keys, settings, and the library in ~/.config/polaris.
If Welcome reports an error after an upgrade, open
https://localhost:47990/#/login and use the previous credentials. On hosts
whose guide uses the IPv4 loopback explicitly, use
https://127.0.0.1:47990/#/login. Do not delete the configuration directory just
to recover web access.
If the old credentials are no longer known, reset them as described below.
Reset the web UI username and password:
polaris --creds new-username new-passwordRun this as the same user account that runs Polaris. Do not use sudo unless Polaris itself runs
as root, because that can update a different config directory.
Restart Polaris after changing credentials. A running Polaris process keeps the previous credentials in memory until restart.
For packaged user-service installs:
systemctl --user restart polarisFor foreground sessions, stop Polaris and start it again.
Service does not start, or the console keeps an old version
Section titled “Service does not start, or the console keeps an old version”systemctl --user status polaris reporting status=203/EXEC, or a console that still shows the
previous version after a package update, usually means the user service is not running the
packaged binary. The Bazzite DRM/KMS recipe points the service at a copy under /usr/local
through a drop-in: delete the copy without the drop-in and the service execs a path that no
longer exists; update the package and the copy silently stays on the old version.
systemctl --user cat polaris shows the drop-in and its ExecStart. sudo -H polaris --setup-host
reports both cases with the fix, the Update Center says “Installed, running a copy” instead of
asking for a restart that would change nothing, and the Doctor’s running_binary row (also in
the support bundle) names the binary that produced the report.
To run the packaged binary again:
systemctl --user stop polarisrm -f ~/.config/systemd/user/polaris.service.d/10-bazzite-kms.confsystemctl --user daemon-reloadsystemctl --user start polarisTo keep the copy, refresh it after every update as the
Bazzite guide describes. On rpm-ostree hosts the console
also shows the old version until the new deployment is booted; rpm-ostree status marks the
booted one with ●.
Restart from the console or the tray
Section titled “Restart from the console or the tray”Under polaris.service, a restart asked for from the console or the tray exits with status 75
and the unit starts the installed binary again a few seconds later, so a restart after a package
update runs the new version. Started any other way, Polaris re-executes itself in place and keeps
its process id. systemctl --user stop and restart always win over a pending restart: the
process exits instead of re-executing, so a stop cannot hang until systemd’s timeout.
A custom unit with its own Restart= can opt into the same behaviour with
Environment=POLARIS_SERVICE_RESTART=1; POLARIS_SERVICE_RESTART=0 keeps the in-place restart
under any unit.
In 1.4.8 a restart request could be lost when the host happened to be running a shell command at
that moment. The console then kept the old settings and the log ended at
“Shutdown requested: restart requested”. On such a host, systemctl --user restart polaris still
works.
Web UI does not load
Section titled “Web UI does not load”- Confirm Polaris is running.
- Check that you are opening
https://localhost:47990orhttps://localhost:<port + 1>if you changedport. - Accept the local HTTPS certificate warning in the browser.
- Check your local firewall rules if the UI is unreachable from another device on the LAN.
Polaris and Sunshine on the same host
Section titled “Polaris and Sunshine on the same host”Polaris keeps its config under ~/.config/polaris, so installing it should not remove or overwrite
an existing Sunshine setup. Do not run both hosts on the default GameStream/Moonlight ports at the
same time unless you intentionally change one host’s port value.
For a quick Polaris test, stop Sunshine first:
systemctl --user stop sunshinesystemctl --user enable --now polarisIf Sunshine runs as a system service on your distro, use the matching system-service command instead. To switch back, stop Polaris and start Sunshine again.
Paired client gets Permission denied (403) when starting a stream
Section titled “Paired client gets Permission denied (403) when starting a stream”Pairing proves the device’s identity, but its saved access preset still controls whether it can start or control a stream. A device set to Browse & Watch can list apps and watch an existing session, but Desktop and game launch requests correctly return 403.
Open Devices in the Polaris web UI, find the paired device, choose Edit Access, select Game Control, and save. Retry the launch without pairing again. Game Control includes library, launch, keyboard, mouse, touch, pen, and controller input, but does not grant clipboard, file-transfer, or server-command access. Choose Full Control only when the device needs those broader operations.
Polaris now uses Game Control for newly paired Nova and Moonlight-compatible devices. Existing paired devices are never silently upgraded, so a device saved by an older release may still need the one-time access change above.
Cursor is missing with DRM/KMS capture
Section titled “Cursor is missing with DRM/KMS capture”DRM/KMS exposes the desktop framebuffer and the hardware cursor as separate planes. Polaris must composite that cursor plane into the video stream; otherwise the pointer can remain visible on the host display while disappearing in Moonlight.
Open Settings > Input > Pointer and touch and enable Show host cursor. New or unset
configurations enable it by default, while an explicit mouse_cursor_visible = disabled remains
unchanged. You can toggle Polaris-controlled cursors for the current runtime with
Ctrl+Alt+Shift+N. If a client draws its own local cursor and you see two pointers, disable the host
cursor. Portal capture may embed the compositor cursor independently of this runtime toggle.
Headless session does not start cleanly
Section titled “Headless session does not start cleanly”Confirm these settings first:
headless_mode = enabledlinux_use_cage_compositor = enabledlinux_prefer_gpu_native_capture = enabledThat is the intended Headless Stream path for NVIDIA/NVENC and AMD/Mesa VAAPI hosts that can keep
frames GPU-resident. It avoids touching your normal desktop layout and reduces display mode churn
after a session ends. If the stream is stable but logs report SHM/RAM capture, treat that as a
performance/capability fallback first, not a startup failure. If enabling GPU-native capture blocks
launch on a specific driver/compositor stack, temporarily set it to disabled and include the
capture decision fields in the bug report.
The built-in Desktop entry streams your existing KDE, GNOME, or wlroots desktop even when the host default is a private stream, because it has Mirror the host desktop turned on in the app editor. An entry with no command and that setting off opens this private compositor instead. If such an entry connects but shows an empty or black desktop while app entries work, the headless runtime is alive and nothing visible has been launched in it yet; right-click the empty screen to open the session menu.
Unsure which mode you should be running in the first place? Start with Launch modes and capture paths.
NVIDIA true-headless first launch fails with 503
Section titled “NVIDIA true-headless first launch fails with 503”On NVIDIA/NVENC hosts running true-headless labwc with GPU-native capture disabled, the very first
launch can fail with a 503 encoder-initialization error even though NVENC is healthy. On headless
paths the encoder probe is deferred and served from an on-disk cache; when that cache is cold or
missing, priming it can fail and the launch is refused. The host configuration warnings surface this
as nvidia_headless_gpu_native_disabled.
The fix matches the warning’s own advice: set linux_prefer_gpu_native_capture = enabled (or pick
the Private Stream (GPU-native) card in the web UI), restart Polaris, and retry, before chasing
CUDA or NVENC driver issues.
Fullscreen Proton or Wine game renders on the physical monitor
Section titled “Fullscreen Proton or Wine game renders on the physical monitor”The stream connects, audio and input reach the private session correctly, and the client shows an empty compositor while the game appears on your real desktop instead.
If the app is launched through Flatpak, this is the Flatpak portal replacing DISPLAY. Polaris
exports the private session’s display to the command it launches, but when that command spawns back
out through the portal (org.freedesktop.portal.Flatpak), the portal builds the new sandbox from
the portal service’s own environment. The portal service is D-Bus activated and holds the display
your desktop session had at login, so it overwrites DISPLAY and binds only that one X socket into
the container. Wine and Proton use the X11 driver by default, follow the substituted DISPLAY, and
land on the host desktop.
Nothing set at any layer above the portal survives this. Passing the variable explicitly does not help either, because the portal applies its own X11 arguments after the caller’s environment.
Confirm it in one command while the game is running, using the game’s own mount namespace:
GAME=<game-pid>sudo ls -la /proc/$GAME/root/tmp/.X11-unix/sudo tr '\0' '\n' < /proc/$GAME/environ | grep -E '^(DISPLAY|WAYLAND_DISPLAY)='If the only socket present is the one for your desktop session rather than the private session’s, the container never had a path to the private display.
Workaround: launch through a native, non-Flatpak build of your launcher (umu-launcher, Lutris,
Heroic, Steam). The Steam Runtime container itself honors DISPLAY correctly, so removing the
portal hop is enough. A direct flatpak run also passes the display through correctly; it is
specifically the portal spawn underneath a Flatpak launcher that does not.
Two things commonly trip up the switch from a launcher’s Flatpak build to its native one:
- Re-add the game rather than reusing its old ID. Heroic gives sideload entries a new
app_nameper install, so an identifier copied from the Flatpak install will not resolve in the native one. Read the current value out of~/.config/heroic/sideload_apps/library.json, or your launcher’s equivalent. - Let the launcher finish starting before handing it a launch URL. Native Heroic given a
heroic://launch?...URL on a cold start throwsCannot read properties of undefined (reading 'getGame')rather than queuing the request. Start the launcher on its own first and send the launch as a separate command.
A working app entry on the reporting host ended up as:
WAYLAND_DISPLAY=wayland-1 DISPLAY=:2 heroic --no-gui "heroic://launch?appName=<id>&runner=sideload"Polaris exports both of those variables into the private session already, so setting them by hand is redundant rather than required. They are shown here because that is the entry that was verified.
Polaris logs a warning at launch when an app command can reach the portal. If the private compositor does not expose a managed window within the observation period, Polaris reports that it could not confirm attachment. Check the client image before applying the portal workaround: some fullscreen or XWayland surfaces can be visible even when the compositor’s managed-window detector cannot enumerate them, while a genuinely empty client image still points to the host-desktop escape described above.
Steam Big Picture black screen or tiny window
Section titled “Steam Big Picture black screen or tiny window”Clear Steam’s HTML cache:
rm -rf ~/.local/share/Steam/config/htmlcache/If you are using MangoHud, disable it for Steam Big Picture and Steam/Proton launches first. Those paths are the most sensitive to early helper-process crashes.
Input does not work
Section titled “Input does not work”The udev rules and modules-load configuration ship as package files, so virtual input works after the next reboot with nothing else to run. To use it without rebooting first, load the modules once:
sudo modprobe uinput uhidIf the problem persists, ensure your user has access to the input stack expected by your distro setup:
sudo usermod -aG input "$USER"Then sign out and back in.
On an ostree host — Bazzite, Bluefin, Silverblue and relatives — that command does nothing
useful: the input group lives in /usr/lib/group rather than /etc/group, so usermod
finds no group to add anyone to. Universal Blue images ship a recipe that copies the
definition across first:
ujust add-user-to-input-groupPolaris detects this and prints whichever command applies to your host.
If Polaris was installed before the rules became package files, an older copy may still sit in
/etc/udev/rules.d/60-polaris.rules. /etc overrides the packaged file, so that copy keeps
shadowing later fixes — including the seat isolation rules, which then never apply no matter what
the configuration says.
sudo -H polaris --setup-hostHost setup removes that copy only when its contents still match the file this Polaris ships. An older version’s copy does not match — that is what upgrading changed — so it is kept, with a warning naming the file, because nothing can tell it apart from a copy you edited yourself. Upgrading is therefore the case most likely to leave a shadowing file behind.
If you did not edit it, remove it and reload:
sudo rm /etc/udev/rules.d/60-polaris.rulessudo udevadm control --reload-rulesThen confirm the packaged rules are the ones in effect:
grep -c seat-isolated /usr/lib/udev/rules.d/60-polaris.rulesClient input also types into the host desktop
Section titled “Client input also types into the host desktop”A private stream session and the desktop session logged in at the machine both see the virtual
keyboard and mouse Polaris creates, so a client’s typing reaches both. Enable
client_keyboard_mouse_seat_isolation to assign those devices to a dedicated seat, or ignore them
by name in your desktop compositor. See
host and private session input isolation.
Local desktop audio is captured during a headless stream
Section titled “Local desktop audio is captured during a headless stream”In headless labwc sessions, Polaris routes launched apps to the Polaris virtual sink and captures
that sink directly instead of changing the user’s global default audio output. The healthy log path
looks like:
Linux audio isolation: routing launched apps to virtual sink [sink-sunshine-stereo] without changing the user's default sinkLinux audio isolation: capturing virtual sink without changing the user's default sinkIf local Plasma/GNOME audio is still mixed into the stream, include the audio section of the logs and whether the client requested host audio. Host-audio mode intentionally captures the host sink, so same-user local apps can still be part of that stream.
Thread priority warning during a stream
Section titled “Thread priority warning during a stream”If the log shows this warning during an otherwise working stream:
Thread priority elevation unavailable; continuing with the default schedulerPolaris is running, but the user service cannot raise capture, encode, or audio worker priority.
Packaged installs include LimitRTPRIO=95 and LimitNICE=-10 in polaris.service; reload the user
manager and restart Polaris after updating the package:
systemctl --user daemon-reloadsystemctl --user restart polarisIf the warning remains, the user manager inherited stricter limits from the login session. Confirm the active unit with:
systemctl --user cat polarisjournalctl --user -u polaris -b --no-pager | grep -E 'Thread priority|RealtimeKit|SCHED_FIFO'Installing and running RealtimeKit can also allow priority elevation without granting broad capabilities to the Polaris binary.
The Doctor reports this as a host configuration warning too, with the limits that applied, so a support bundle exported after the stream still has it.
Packaged Linux user units are ordered with graphical-session.target and pass through common
desktop environment variables such as WAYLAND_DISPLAY, XDG_RUNTIME_DIR, and
DBUS_SESSION_BUS_ADDRESS. In private Headless Stream mode, a missing parent WAYLAND_DISPLAY
is logged as a limited desktop-preview/portal warning instead of a stream startup failure because
Polaris starts its own labwc Wayland socket for the client session.
KMS capture refused for a missing capability
Section titled “KMS capture refused for a missing capability”KMS/DRM capture reads framebuffers straight from the kernel, which needs CAP_SYS_ADMIN on the
Polaris binary. That is deliberately opt-in: the package does not grant it, the host setup step
does. Installing or updating the package replaces the binary, and the new one does not carry the
capability, so run the step again after every install or update. With capture = kms and no capability, Polaris finds the display, logs
Failed to gain CAP_SYS_ADMIN and Couldn't get handle for DRM Framebuffer, and then either
substitutes another backend or, when nothing else can capture, serves with no capture at all and
H.264 as the only codec. The Doctor reports both cases as kms_capture_needs_capability.
sudo -H polaris --setup-host --enable-kmsthen restart Polaris. KMS capture is the path that carries HDR, so keep it if HDR is the goal.
NVIDIA KMS capture issues
Section titled “NVIDIA KMS capture issues”If KMS capture gives a black screen on NVIDIA, confirm the kernel is using:
nvidia_drm.modeset=1If you do not need DRM/KMS capture, keep using the default compositor and portal paths instead.
A startup warning that says KMS probe could not access DRM framebuffer handles; continuing with non-KMS capture backends when available is informational for portal/compositor users; do not apply
setcap unless you intentionally selected KMS capture.
If a manually copied explicit-KMS test binary still logs Failed to gain CAP_SYS_ADMIN after
setcap, check the mount options for the binary path. File capabilities are ignored on nosuid
mounts, so /tmp builds can be misleading; copy the test binary to a normal path such as
/usr/local/bin before applying setcap.
For low-FPS NVIDIA headless reports, check Build features: cuda=... first. If the log says
cuda=disabled and later shows Attempting to use NVENC without CUDA support. Reverting back to GPU -> RAM -> GPU, the stream is taking an extra CPU copy/upload path. Use a CUDA-enabled package
or rebuild with -DPOLARIS_ENABLE_CUDA=ON before comparing headless performance against Sunshine.
The host Doctor reports the same fact before any stream as capture_copies_through_system_memory
with cause build_without_cuda; see Capture is on the CPU.
The expected fast-path markers for NVIDIA true-headless testing look like this:
Build features: cuda=enabledlabwc: Starting in headless modewlr: Using ext-image-copy-capture DMA-BUF for headless labwccapture_transport=dmabuf frame_residency=gputarget_device=cuda target_residency=gpudisplay_preview: Failed to capture cage screenshot is the web dashboard preview path, not the
stream capture path. Repeated failures are rate-limited in the log, and the dashboard backs off
preview refreshes after failed captures. If the preview is missing, confirm grim is installed with
command -v grim.
For what the capture paths mean in plain terms and which mode fits your GPU, see
Launch modes and capture paths. For capture performance, check
/polaris/v1/session/status; its capture object includes
path, reason, reason_message, cpu_copy, gpu_native, and nested decision fields.
/polaris/v1/stream-policy exposes the same data as capture_path, capture_path_reason,
capture_path_reason_message, capture_cpu_copy, capture_gpu_native, and capture_decision.
A reason such as headless_shm_fallback means Headless Stream is healthy enough to run but still
using the conservative SHM/system-memory path. headless_extcopy_dmabuf is the true-headless
DMA-BUF path, and gpu_native_requested_shm_fallback means GPU-native capture was requested but
the Wayland capture path still fell back to SHM. Support bundles include the same normalized
decision data under capture.decision and stream stats capture_decision so a report captures
the selected path, reason message, transport, residency, runtime backend, effective headless
state, and GPU-native override state.
For LTS distro expectations and package caveats, see the Linux LTS Headless Fallback Matrix. Xvfb or gamescope should be treated as investigation-only unless this supported labwc path cannot cover a confirmed target environment.
Whole-machine freeze on a hybrid laptop
Section titled “Whole-machine freeze on a hybrid laptop”A freeze that needs the power button is a kernel or GPU driver lockup, not a Polaris crash, and the journal usually has nothing after it. On a laptop with an integrated GPU and an NVIDIA card there are three drivers in play during a Mirror Desktop stream: the iGPU reading the screen back for the compositor’s screencast, the NVIDIA card taking the frames for NVENC, and the compositor serving both. Test in this order, one change at a time, and stream after each:
- Is the machine dead or only the screen? Toggle Caps Lock, or ping the host from a phone. A
live host can be reached over SSH during the “freeze” and
journalctl -k -fnames the driver. - Keep the web console closed during the test. It used to poll the display list through the compositor every three seconds; current releases cache it, older ones do not.
- Intel iGPU: add
intel_iommu=igfx_offto the kernel command line and reboot. Comet Lake and nearby generations are known to hard-freeze with VT-d active for graphics. - Take the NVIDIA card out:
adapter_name = /dev/dri/renderD128(the compositor’s node, the Doctor names it) andencoder = vaapi. No freeze means the NVIDIA side is the trigger. - Keep NVENC but stop the sleep/wake cycle:
options nvidia NVreg_DynamicPowerManagement=0x00in/etc/modprobe.d/nvidia-pm.conf, rebuild the initramfs, reboot.
Unload any other streaming stack’s virtual display module first (lsmod | grep -E 'hermes|vibeshine|evdi'),
so there is one variable fewer. After the next freeze, before starting Polaris again, keep
~/.config/polaris/polaris.log.backup: it is the run that was streaming, and Polaris overwrites it
at the next start.
Capture is on the CPU
Section titled “Capture is on the CPU”Mission Control reads SHM or system memory, the Doctor’s capture row says shm_cpu_capture,
and encode times sit at 4 ms and up where a GPU-native stream would show about 1 ms. The host
Doctor now says this before the first stream, as capture_copies_through_system_memory with
a cause, read from the configuration, the build and the capture backend the host selected at
startup. Each cause has one fix.
| cause | what is happening | fix |
|---|---|---|
build_without_cuda |
The binary was built without CUDA, so on NVIDIA every capture path copies each frame through system memory before NVENC. polaris --version prints Build features: cuda=disabled; each session logs Attempting to use NVENC without CUDA support. Reverting back to GPU -> RAM -> GPU. Stream mode and the GPU-native setting cannot change it. |
Install a package built with CUDA. The official Fedora, Arch and Ubuntu packages are; a source build needs -DPOLARIS_ENABLE_CUDA=ON. Only the NVIDIA driver is needed at run time, not the toolkit. |
x11_capture |
The host session is X11 and capture runs through x11grab, which is a system-memory path by construction. |
Stream from a Wayland session, or use a Private Stream mode, which captures Polaris’ own compositor. capture = nvfbc keeps X11 capture on the GPU on NVIDIA cards that expose NvFBC. |
headless_dmabuf_unavailable |
Private Stream runs the hidden headless compositor, and its last attempt on this host could not hand frames over as DMA-BUF, so capture fell back to SHM. | Pick Private Stream (GPU-native) in Play Setup for one launch, or set linux_prefer_gpu_native_capture = enabled and restart: Polaris then runs the private compositor windowed, where DMA-BUF capture works. |
windowed_dmabuf_unavailable |
The private compositor already runs windowed for GPU capture and the last DMA-BUF probe failed. | This path needs wlr-export-dmabuf from labwc and a driver that can import the buffer. Send a support bundle from one stream; it carries the import error. |
vaapi_system_memory_by_design |
AMD and Intel: every VA-API capture path takes one copy per frame on purpose, because the DMA-BUF import into the encoder has crashed or stalled on AMD hosts (#367) and stays off until affected hosts prove it safe. Reported as info. |
Nothing. If throughput falls short at high resolution or refresh, lower resolution, frame rate or bitrate first. POLARIS_PORTAL_DMABUF=1 opts the portal path into the unvalidated DMA-BUF route with no automatic fallback. |
Mirror Desktop and Host Virtual Display on KDE or GNOME capture through the desktop portal.
With CUDA or Vulkan the portal is asked for DMA-BUF and the compositor decides; KDE handed over
system memory in testing. The forecast says nothing for that case, and the session’s
capture_transport= log line says which it got.
The forecast is silent until Polaris has evaluated its capture backends at startup, and it can
only tell NVIDIA from AMD once an encoder is chosen: with encoder left on auto and a headless
mode, that is the first launch. linux_gpu_profile.capture_forecast in the support bundle
carries the backend, the encoder, build_has_cuda and the verdict.
VAAPI or software encode fallback
Section titled “VAAPI or software encode fallback”If Polaris cannot hold the preferred hardware path, open Mission Control or Troubleshooting and check the active runtime path. Polaris surfaces when capture or encode falls back so you do not need to guess from a black-box client session.
Linux HDR or Main10 has wrong colors
Section titled “Linux HDR or Main10 has wrong colors”If the log says stream_hdr_enabled=false, treat that stream as SDR. A client HDR request or
hdr_mode = 2 can still move the encoder into a 10-bit/P010 path, but it does not make a non-HDR
Linux capture path into a true HDR source. On AMD VAAPI systems, keep hdr_mode = 0 and disable
client HDR requests until SDR colors are correct, then test HEVC Main 8-bit before testing Main10.
For true HDR, look for all of these lines in the same launch:
HDR metadata: available=true usable=trueColor coding: HDR (Rec. 2020 + SMPTE 2084 PQ)HDR decision: ... display_hdr=true hdr_metadata_available=true stream_hdr_enabled=trueIf stream_hdr_enabled=false, Polaris is being conservative: the client may have requested HDR or Main10,
but the active Linux display path did not provide enough metadata to advertise a real HDR stream.
If usable=false, the display path exposed an HDR metadata blob, but Polaris rejected it because core
static metadata such as display primaries or max display luminance was missing.
HDR never engages
Section titled “HDR never engages”stream_hdr_enabled=false on every launch, whatever you toggle, is six independent gates and
any one of them is enough. Check them in this order; each has a line in
journalctl --user -u polaris that names it.
| gate | what the journal says | fix |
|---|---|---|
| capture backend cannot report HDR | HDR decision: ... display_hdr=false with capture = wlr or unset on a private mode |
capture = kms |
| stream mode captures Polaris’ own compositor | session_runtime: ... effective_headless=true |
Mirror Desktop, Host Virtual Display, Desktop Takeover or Gamescope |
binary lacks CAP_SYS_ADMIN |
Failed to gain CAP_SYS_ADMIN, Couldn't get handle for DRM Framebuffer [...]: Probably not permitted |
sudo -H polaris --setup-host --enable-kms, restart |
| client forced off on the host | Doctor hdr_disabled_by_saved_setting; client_profiles.json hdr: false or device_db.json hdr_capable: false |
clear both, or let the client’s own HDR10 report win (1.4.8) |
| client never asked | portal HDR force -> 0 from enable_hdr=false, client_dynamic_range=0 |
turn on Request HDR in the client; in Nova it is off by default |
| Host Virtual Display got a KWin screen | Doctor hdr_unavailable_on_kwin_virtual_screen; HDR was requested, and a KWin virtual screen carries none |
KWin virtual screens have no HDR; use Mirror Desktop from an HDR monitor with capture = kms |
When all six pass, the session logs HDR metadata: available=true usable=true,
Color coding: HDR (Rec. 2020 + SMPTE 2084 PQ) and stream_hdr_enabled=true after
Session started for [...]. The encoder probe logs the same lines earlier even when the session
will not, so read the ones after the session starts.
Host Virtual Display on KDE
Section titled “Host Virtual Display on KDE”On KDE Plasma 6, Host Virtual Display gets a new screen from KWin (see
Launch modes and capture paths). The Virtual Display panel on the Audio/Video
tab names the backend in use and, when it cannot run, why. The Doctor names three problems here
on its own: hvd_kwin_screen_unused when Plasma got another backend, and why;
hvd_screen_scaled when the stream screen runs at a scale other than 100%; and
hvd_input_not_mapped when touch or pen could not be put on the stream screen.
- KWin does not offer its screencast protocol to Polaris. KWin only offers it to a program a
desktop entry names. Polaris writes one to
~/.local/share/applicationsthe first time and waits a few seconds for KWin to pick it up. If a launch still misses it, Polaris checks again after half a minute, so try again then. If it keeps failing, check that the entry exists and that itsExecline is the Polaris binary that is running. - The stream runs at 60 Hz when the client asked for more. A KWin screen starts with one
60 Hz mode, and Polaris adds the client’s rate as a custom mode with kscreen-doctor. The log
says
did not take modewhen KWin refused it. - KWin says it cannot tell which program Polaris is, or the automatic order borrows a monitor
instead. KWin offers a screen only to a program it can match to its permission entry, and it
cannot match one that holds file capabilities, such as the
cap_sys_adminthat--enable-kmsgrants for KMS capture. On the automatic order such a Polaris skips the KWin screen and falls back to EVDI when it can create a display, otherwise to kscreen-doctor. Set Backend to KWin (linux_virtual_display_backend = kwin), leavecaptureonautoorportal, and restart Polaris: it then drops the capability at start. KMS capture and a KWin screen do not go together. - kscreen-doctor is not installed. It comes with Plasma. Polaris needs it to place the screen beside your monitors at scale 1; without it KWin can put a new screen on top of your monitor.
- A game opened on my monitor instead of the stream. Check the log for the backend: with
using backend: EVDIon Plasma, Backend is set toevdior the KWin screen could not start, and an EVDI screen is a monitor like any other, so games open on your primary one; set Backend back to Automatic or to KWin. On the KWin screen, Polaris moves windows onto the stream screen with a small KWin script it loads for the stream. The log saysnew windows will not be movedwhen KWin refused to load it or dropped it; the stream then shows an empty screen while the game runs on your monitor. With two Polaris screens at once, the newer one gets new windows. A window the script moves also gets the focus, so the controller drives the game from the first press. - A tap from the client landed on another monitor, or the game ignored it. KWin spreads a touch
screen and a pen over every monitor unless each is tied to one, so Polaris ties the client’s to
the stream screen while it exists (the log says
lands on) and unties them when it goes. KWin remembers the tie in~/.config/kcminputrcunder each device’s name, so at the start of every stream on Plasma Polaris also undoes a tie to one of its own screens that a crash left behind; a tie you made yourself in System Settings is kept. The Doctor’shvd_input_not_mappedcarries KWin’s answer when the tie failed; the usual cause is a Polaris started outside the Plasma session, which cannot reach KWin on that session’s bus. A mouse in absolute mode is not tied: KWin places it over the whole desktop whatever it is told, so use touch, or relative mouse mode, on a desk with more than one monitor. - My monitors moved when the stream started. KWin keeps a layout for each set of screens and
applies it when the set changes, and the one it kept for “your monitors plus the stream screen”
can have your monitors somewhere else. Polaris puts every monitor back where it was in the same
step that places the stream screen, and the log says
did not go back where they werewhen KWin would not. When the stream ends KWin applies the layout it kept for your monitors alone. - The stream screen is at 135% (or another scale). An EVDI screen on Plasma gets the scale KWin
stored for it, and the Doctor says
hvd_screen_scaled. While a stream runs, set that screen to 100% under System Settings, Display & Monitor; KWin keeps it for next time. Polaris’s own KWin screen is always put at 100%. - A window I opened at the desk jumped to the stream. While a stream runs, every new application window is moved onto its screen. Drag it back, or use Meta+Shift+Left. The desktop’s own prompts stay on your monitor: polkit password prompts, ksshaskpass, KWallet and KRunner.
- My second monitor’s desktop or panel moved. Plasma gives each screen rank its own desktop
and panel. Polaris ranks the stream screen after all of yours and keeps your screens in their
order; the log says
not ranked as they werewhen KWin would not keep that ranking. - Choosing another backend. Set Backend on the Virtual Display panel
(
linux_virtual_display_backend) toevdiorkscreento use that one instead; the choice applies without a restart.
A client cannot pick its resolution
Section titled “A client cannot pick its resolution”A device with a Display Mode Override saved on the host gets that mode whatever it asks for, so choosing 1080p on the client changes nothing. When the override replaced a different request, the Doctor checklist shows a Display mode warning naming the mode the client asked for, and Session Snapshot’s Display mode tile shows both. Clear Display Mode Override for that device on the Devices page, or set it to the mode you want; see Devices. A launch that carries the client’s own resolved profile, as Nova’s does, keeps the client’s mode, so the same device can behave differently in Nova and Moonlight.
Stutter over Tailscale
Section titled “Stutter over Tailscale”Session Snapshot’s Network path tile says how the client reached this host. The Doctor and
support bundles carry the same thing as client_network_path: lan, cgnat (the shared
100.64.0.0/10 range, which Tailscale uses on IPv4), tailscale (its IPv6 range), link-local,
public or loopback. It stays after the stream ends, so a support bundle exported afterwards
still has it, and it never records the address itself.
A tailnet stream is fine while Tailscale connects the two machines directly. When it cannot, it
relays the traffic through its DERP servers, and the stream stutters and its latency climbs. Run
tailscale ping with the host’s name on the client: via DERP in the answer means the stream is
relayed, an address and port mean it is direct.
A launch was refused
Section titled “A launch was refused”A client that could not start a stream used to see error 503 and one generic sentence,
whatever the host knew. The host now sends the reason as the message the client shows, with
the one change that fixes it, and Nova shows both in its launch sheet. The error_code names
below are stable, so they can be searched for here and in support threads.
| error_code | what happened on the host | fix |
|---|---|---|
encoder_probe_failed |
No video encoder could start; on NVIDIA the message adds the driver detail when the driver is the reason | Check the Doctor’s Encoder and Capture rows. Against the private compositor: pick Private Stream (GPU-native) or set linux_prefer_gpu_native_capture = enabled |
no_capture_backend |
No capture backend works in the configured stream mode, so nothing could be probed | Check capture against the stream mode; unset lets Polaris pick. The Doctor names the missing protocol |
capture_backend_unavailable |
The launch asks for a capture backend that cannot capture anything in its stream mode, such as capture = wlr in Mirror Desktop on KDE or GNOME |
Set Force a Specific Capture Method under Advanced to Autodetect, or use a stream mode that backend can serve |
kms_capture_needs_capability |
capture = kms without CAP_SYS_ADMIN on the binary |
sudo -H polaris --setup-host --enable-kms, restart |
desktop_capture_not_prepared |
The screen sharing prompt was declined, or desktop capture could not be prepared | Approve the prompt on the host desktop, or use a Private Stream mode |
private_runtime_unavailable |
labwc (or gamescope) is not installed for the chosen mode | Install it, or use Mirror Desktop |
private_runtime_start_failed, private_runtime_socket_missing |
The private compositor did not start, or started without a Wayland socket | The host journal has the compositor’s own error; restart Polaris and retry |
gamescope_session_failed |
The nested gamescope session did not start, or timed out | Check gamescope on the host; the Doctor has a Gamescope helper report |
virtual_display_failed, virtual_display_unavailable |
Host Virtual Display could not be created, or no backend exists | The message carries the reason (usually the evdi module); Private Stream needs no virtual display |
desktop_takeover_failed, desktop_takeover_recovery_pending |
Desktop Takeover could not start, or the previous one is still restoring the display | Wait for the host display to return; the Doctor’s display warning names the reason |
session_stopping, session_state_changed, launch_cancelled, previous_session_cleanup_pending, steam_shutdown_pending, virtual_display_recovery_pending |
The previous session, Steam, or a display is still being torn down | Wait a few seconds and launch again; restart Polaris if it persists |
emulator_not_installed |
A game imported from a ROM folder names an emulator this host does not have, or the emulator file the folder points at is gone | Install the emulator from ROM folders under Import Games (Flathub emulators have an install button), or put the folder’s emulator file back |
child_tracking_failed, no_active_session |
The app started but could not be tracked, or a resume found nothing to resume | Launch again; send a support bundle if it repeats |
Moonlight clients see the same message in their own error dialog; only the code and action attributes are Nova’s.
Quick recovery ladder
Section titled “Quick recovery ladder”The Doctor & Support page offers three recovery actions, ordered from least to most disruptive. Try them in order and stop at the first one that gets the stream back.
- Force Close ends the running app. Use it when a client says an app is already running or a game hangs on the host while Polaris itself is fine. Connected clients see the stream end or return to the app list, and the app list reloads.
- Restart Polaris restarts the host service. Use it when the host stops answering, pairing fails, or capture never starts. Every active stream ends and the web UI reconnects on its own once the service is back.
- Quit Polaris stops the service entirely. Use it only when you can start Polaris again another way, such as a terminal or the desktop launcher, because the web UI cannot bring it back once it has exited.
On Windows the ladder has an optional fourth step, Reset Persistent Display Device Settings, for a host stuck restoring a changed display configuration.
Built-in self tests
Section titled “Built-in self tests”The self tests on the Doctor & Support page answer three player questions without opening the raw evidence.
- Network Path Tester reads the live round trip and packet loss for the current client, checks whether the control and stream ports answer, and suggests a bitrate ceiling the measured path can carry. Open Advanced evidence for the per-check detail and the native probe output.
- Controller/Input Tester shows whether the browser or the host sees a controller. Detect controller samples the browser gamepad API, Log A press records a manual button sample, and Test rumble pulses haptics when the browser exposes them.
- Post-session Stream Report summarises the last completed stream: the loudest signal, who owned the issue, and the launch profile to try next.
Copy self-test summary puts all three results on the clipboard in the same form the support bundle uses.
Support bundle and logs
Section titled “Support bundle and logs”The Troubleshooting screen in the Polaris web UI inspects this host. It is the place to go when something is wrong and you want to know what Polaris thinks happened. The Troubleshooting guide on papi-ux.com explains the problems themselves and is the place to go when you know what is wrong and want to fix it.
When reporting a bug, open Troubleshooting and use Report a problem. It downloads a redacted support bundle and opens a pre-filled GitHub issue with your host OS, GPU and driver, client, and runtime already answered. Attach the bundle to that issue.
Nothing is sent anywhere on its own. The bundle lands on your machine and the issue opens as a draft you complete, so you see exactly what you are sharing before anyone else does.
The bundle carries the current run’s log, the two runs before it (polaris.log.backup and
polaris.log.backup.1 next to the log), and the kernel’s GPU-related lines for this boot and the
previous one when the journal is readable by your account. A freeze, a reboot and an export used
to cost the run that had been streaming; it is now the second retained run. When the journal is
closed to your account the bundle says so and names the command to run instead.
If you would rather assemble it yourself, the same screen offers the bundle and the issue draft as separate downloads, and describing the active route, capture backend, encoder, and client device by hand is still useful.
What is redacted
Section titled “What is redacted”Values whose name reads as a credential are replaced before anything leaves the browser: whole
words such as password, token, secret, cookie, auth and credential, run-together forms such as
apikey, and key when something qualifies it, as in api_key. Names that merely contain one of those,
such as keyboard, stay readable so the bundle remains worth reading. The exact rule is stated in
the bundle itself under redaction_notice.
Network addresses are replaced too, in the bundle, the pre-filled issue and copied support text.
Each one becomes a label that keeps what kind of address it was, such as [lan-1], [cgnat-1]
(the range Tailscale uses on IPv4), [tailscale-1], [link-local-1] or [public-1], and the same
address keeps the same label throughout one export. That keeps the part that usually matters, such
as a client that moved from your LAN to a tailnet, without saying which address it had. Loopback,
multicast and example addresses stay as they are.
Redaction is not a promise that a bundle is safe to publish unread. Look at it first.
Reporting a crash
Section titled “Reporting a crash”Polaris records how each run ended, so the first question is already answered for you. Open Troubleshooting after restarting: if the previous run did not shut down normally, the page says so at the top, and reports one of
- crashed, meaning it died on a named fatal signal, with the captured backtrace,
- unclean, meaning it never recorded an exit and left no crash evidence, which usually means
the OOM killer, a
SIGKILL, or power loss rather than a fault in Polaris.
That distinction matters and is hard to make by hand, which is why Polaris makes it.
The log of the run that crashed is preserved too. The active log describes the run you are looking at now, so the interesting one is the retained copy from the previous run, and the support bundle carries it.
Getting a symbolised backtrace
Section titled “Getting a symbolised backtrace”The backtrace Polaris captures comes from a stripped binary, so it names addresses more than
functions. For a symbolised one, the matching debug package and coredumpctl still work exactly as
before, because the crash handler re-raises rather than swallowing the signal:
sudo pacman -S polaris-debugcoredumpctl info polarisInclude that verbatim alongside the bundle when a maintainer asks for it.
When the web UI is unavailable
Section titled “When the web UI is unavailable”The host config lives in ~/.config/polaris, the service logs can be captured from your systemd
user journal, and the run-state and crash evidence sit next to the config as last_run.json and
last_crash.txt.