Troubleshooting #
Documentation · Getting started · Configuration
Start with the current stream's settings, the console and the logs. Record the time of a failure before changing anything, and change one setting at a time. Capture and performance probes add work to the GPU; use an idle session for those comparisons.
Find a symptom: Pairing · Console or port · Black screen · Audio · Blurred PyroWave · HDR colour · Stutter · Playnite · RTSS · Display restoration · Controllers · Steam Deck · Updates · Logs and support
What the stream card warnings mean #
The stream card shows the active encoder and warnings for the current session. Each warning also writes a WARN log with its cause and a next step. Capture recovery and audio capture warnings clear when those paths recover. Dropped packets, frames and input, and audio device changes, stay on the card for 30 seconds after they last happened. A warning describes a fallback or limitation, not a measured latency penalty.
- Encoder fallback, software or CPU frame copies: if AMF (AMD) or NVENC (NVIDIA) cannot start, Automatic uses another hardware encoder when one works, such as Quick Sync on an Intel integrated GPU, and says so on the card. It never falls back to software: with no working hardware encoder the stream fails with the AMF/NVENC error in the log. Check that error and the graphics driver. Software must be selected explicitly, preferably at a lower resolution and frame rate; CPU frame copies and software encoding can limit fps.
- Graphics queue conversion/copies: compute setup failed. A busy game can delay capture or conversion. Lower game GPU load or check the AMD driver.
- Desktop Duplication / capture interrupted: WGC or its helper could not capture. UAC and lock screens can cause a temporary change. Unlock the desktop; if the warning persists, check the helper error. A display size change causes capture to reopen.
- Physical display, refresh, HDR or limiter: the requested setup is unavailable, unsupported or owned by another stream. Check the actual display mode and RTSS. A 60 Hz source cannot supply 116 fresh pictures per second.
- Audio: a missing virtual sink can send sound to the host's speakers. Device changes and capture failures can pause sound. The host-loss warning names what was lost: sound Windows dropped because the host's audio sender read too late (a busy CPU or a host problem, not bitrate), or audio packets the network adapter refused (lower the video bitrate or check the adapter).
- Bitrate, pacing or FEC: the encoder/wire budget was limited, packets were dropped, or large frames lost FEC protection. Check Maximum bitrate and the client bitrate. Runtime bitrate updates have a 500 Mbps cap. Leave headroom for audio, packet headers and FEC.
- Input: the selected driver or Windows input API is unavailable. Video continues; repair the named driver or choose a supported profile.
- PyroWave: the client negotiated another codec, or bitrate/recovery protection is insufficient. Use a PyroWave-capable client and a fast wired network, or choose HEVC/AV1.
Moonlight cannot find or pair with the PC #
- Open https://localhost:47990 on the host. If it does not open, check the host/service first.
- Confirm both devices are on the same local network, then try adding the host's local IP address in Moonlight. Guest networks or access-point isolation can prevent devices from reaching each other.
- Check Settings → General → Allow pairing. Enter the four-digit PIN shown by Moonlight into the matching pending request on Devices.
- If discovery alone fails, check Settings → Network for the discovery and bind-address settings. An address restricted to
127.0.0.1is reachable only from the host itself. - If setup reported a firewall-rule failure, read the setup log for the exact error and check Windows Firewall's allowance for the installed
butterpollo.exeon your local network. Avoid disabling the entire firewall to diagnose one application.
If the device pairs but cannot launch Desktop, open Devices → Edit and check its enabled state and List apps, View streams and Launch apps permissions. A device missing mouse or keyboard permission can receive video without that input working.
The console will not open, or a port is occupied #
The default base port is 47989; the console uses 47990. A changed Base port moves the console to the next port up. Check the active profile's sunshine.conf if the launcher reports another address.
Start Rubylight.exe reopens the correct console when that profile is already running. If it reports another streaming host on the port, close the conflicting Sunshine, Apollo, Vibepollo or second Rubylight instance, or assign separate profiles and ports. Restarting another copy on the same port does not solve the conflict.
For an installed host, check Rubylight in Windows Services. Its internal service name is ApolloService. If it is stopped, read service.log before starting it again. The log locations below distinguish service and portable profiles.
Black picture, no display, or WGC fails #
- On Overview → Host readiness, inspect Screen capture, Video encoder and Virtual display. A physical monitor must be active, or the configured virtual display must be available.
- Check Settings → Display → Display and any app/device override. Confirm that the selected display is the one containing the desktop or game.
- For service-mode WGC, keep a Windows user signed in. Rubylight starts its WGC capture worker in that user's session. A locked or UAC desktop uses the Desktop Duplication recovery path; WGC is retried when the normal desktop returns. A stream started while Windows is locked sets up its display on the lock screen; if the virtual display cannot be set up there, the stream shows the physical display and the stream card says so. Start the stream again after signing in.
- Read the
capture backend openedlog entry for the backend that actually opened.requested_capture=wgcdescribes the request and can appear even when capture falls back. - If the virtual-display status reports access denied, use the installed Windows service and check that the bundled driver is ready. A portable host having administrator rights is not equivalent to the driver's service access.
For a controlled comparison, disconnect first, set Settings → Video → Capture method to Desktop Duplication, reconnect at the same resolution/rate, and record whether the symptom changes. Return to Automatic afterward unless the explicit choice is needed. Keep the original failure log so a successful fallback does not hide the WGC error.
HDR looks washed out, too bright, or different between clients #
Separate the host's HDR source, the negotiated stream and the client's display output:
- Check Windows Settings → System → Display for the selected streamed display. HDR on another monitor does not establish HDR on this one; wide-gamut SDR is also different from HDR.
- Check the client's HDR option and the Overview stream card's HDR badge. In the host log,
hdr=true, the codec andsource_pixel=RgbaF16identify a negotiated HDR stream with floating-point capture. FP16 capture alone does not prove that the game is producing HDR highlights. - Start with Settings → HDR → Display HDR: Match the stream and HDR request: Follow the device. A display-only override does not change what the client negotiated. Check app/device overrides too.
- Confirm the game's HDR setting and the receiving display's HDR mode. For a comparison between two clients, keep the codec, scene and host display the same; AV1 HDR on one device versus HEVC SDR on another is not an equivalent check.
- Report whether black levels, mid-grey menus, bright highlights or colour saturation are wrong. Include the client app/version, device and display model, codec, stream resolution/rate and the host log time.
If HDR looks washed out with no HDR badge on the TV, update the host: since 2.0.1 every client gets the HDR state once per stream (#11). Rubylight's native HEVC and AV1 HDR path is checked down to reference pixels; see the native HDR record.
Audio cuts out or lags #
Audio and video share the network path. On Wi-Fi, a video burst can fill the adapter or access point's queue even when the average bitrate looks reasonable. Lost HEVC or AV1 frames can then require a larger recovery frame.
- Lower the client's bitrate and compare the same scene. On AMD, HEVC or AV1 is a better starting point for Wi-Fi than PyroWave. Compare a wired connection when possible.
- Leave
pacing_max_bitrate_kbpsat0for automatic pacing. Conventional codecs use about twice the encoder bitrate only on confirmed Wi-Fi or mobile broadband routes. Ethernet, loopback, VPN, Tailscale and unknown routes keep the faster 800 Mbps default, subject to a known physical Ethernet link's speed. Hyper-V vSwitches use the bound physical adapter when Windows exposes an unambiguous binding; otherwise they keep the wired default. A host connected by cable cannot automatically account for Wi-Fi between the access point and client. - For that wired-host/wireless-client case, try a positive pacing override around twice the stream bitrate: for example,
pacing_max_bitrate_kbps = 120000for a 60 Mbps stream. Save and reconnect. The setting is in kbps, has a floor of 110% of the stream bitrate, and does not reduce the stream's encoded bitrate. If loss continues, lower the client's bitrate too; pacing cannot create wireless capacity.
Compare Moonlight's loss statistics and the host log before and after. reference_invalidations counts reference-loss feedback separately from idr_requests; neither is a count of lost packets. PyroWave has no inter-frame references, so its recovery feedback does not force another encode or increase FEC. A steadily rising request counter alone does not establish Wi-Fi loss. Pacing details and transport evidence describe the limits of the local tests.
PyroWave shows blurred grey blocks #
A very low bitrate can leave PyroWave with only coarse brightness and little colour or fine detail. The old one-bit-per-pixel warning was too low for the measured desktop pictures. The stream card and log now distinguish PyroWave bitrate is too low (red, below the severe-loss floor) from PyroWave bitrate is below recommended (amber, text and textures may lose detail).
At 60 fps the floor/recommendation are 139/277 Mbps for 720p, 187/399 Mbps for 1080p and 747/1593 Mbps for 4K, rounded up. These come from desktop and game test scenes on AMD, including SDR/HDR and 4:2:0/4:4:4; picture content still decides how clean any one stream looks. The one-pixel text at 720p needed more bits per pixel than the larger text at higher resolutions. Criteria, rates and limits explain the two levels.
Raise the bitrate in Moonlight on a fast wired network with headroom for packet overhead and recovery data. 4K60 at the recommended rate needs more than gigabit Ethernet. If the client or network cannot carry it, use HEVC or AV1. Compare the same scene after changing the rate; the warning does not diagnose packet loss or HDR rendering. NVIDIA users should use Vibepollo.
Low latency is reported, but motion still stutters #
Compare the same moving scene at the same resolution, frame rate, codec and bitrate. A static desktop may legitimately send repeated or fewer frames. Begin with 1080p60 SDR, then change one variable.
| Statistic | What it helps distinguish |
|---|---|
| Overview → Frame rate | Frames sent by the host. Repeated pictures can still count toward this rate. |
| Encode (p95) | Slower conversion/encoding completions, including asynchronous encoder work. |
| Host processing | Time after the host claims a frame through preparation for transmission. |
| Frame age | Waiting from the capture/presentation timestamp until the host claims the frame. |
| Moonlight's network and decoder statistics | Loss, jitter or client decoding delays after host processing. |
None of the host timings measures the complete input-to-screen delay. Smoothness also depends on distinct pictures arriving regularly. The performance guide separates host timings, measured picture age, fresh-picture rate and arrival gaps.
If the problem starts when the GPU is fully occupied, compare with a lower game frame cap or lighter graphics settings. If client network loss rises, reduce bitrate or compare a wired connection. If decoder time rises, reduce resolution/rate or select another hardware-decoded codec. Keep WGC and compute defaults for the baseline; use capture/compute overrides only for a recorded comparison. The published local tests do not establish results for every Radeon model or Wi-Fi connection.
The picture freezes while audio and input continue #
Try AV1 first when the client can decode it in hardware, then HEVC.
Start from the default encoder settings: amd_usage=ultralowlatency, amd_lowlatency_mode=auto, amd_input_queue_size=0, amd_preanalysis=disabled, amd_split_frame=auto and compute_queue_realtime=false. Use amd_smart_access_video=disabled while diagnosing a freeze. Install AMD's current Recommended driver for your card from AMD's driver page.
If the freeze returns, change one setting per reconnect (WGC against Desktop Duplication, then gpu_compute_conversion on against off) and note which one changes the result.
Send the host log from CLIENT CONNECTED through CLIENT DISCONNECTED, plus startup/probe failures and the client log around the failure time. The default Information log level already writes encoder settings, display setup and the 5-second stream timings lines. Debug (min_log_level=debug) adds GPU scheduling and probe details; the change applies right away. Preserve whole lines, including fields. In particular include:
stream configured,capture backend opened,compute queue ready for colour conversionand, at debug level,GPU process scheduling configured.AMF encoder settings,AMF split-frame encoding left to the driverorAMF split-frame encoding requested, and everyAMF setting unavailable/AMF split-frame request not acceptedwarning. The HEVC engine field should reportHevcNumOfHwInstances=1on the reported hardware; send the actual value. IncludeLowLatencyInternal,HevcInputQueueSize,HevcEnableEncoderSmartAccessVideo,HevcEnablePreAnalysis, and, for AV1,Av1InputQueueSize,Av1EnableEncoderSmartAccessVideo,Av1EnablePreAnalysisandAv1BPicturesPattern.?means a property could not be read, not that it is disabled.AMF SmartAccess Video disabled because forced low latency can reset the GPUif an imported configuration triggered the guard.- All
stream timingslines, especiallyfps,encode_p95_ms,encode_p99_ms,frame_age_p95_ms,send_interval_p99_ms,idr_requests,reference_invalidationsandbitrate_kbps, alongside Moonlight's network loss and decoder statistics. - Any
GPU device lost: DXGI_ERROR_DEVICE_REMOVED,GPU work did not finish within two seconds,capture copies stopped completing,Encoding failed (…); recreating the same encoder,Encoder output failed (…); recreating the same encoder,The encoder returned no frame for … ms; recreating the same encoderorEncoder recreation failedlines. IncludeAMF compute conversion unavailable (…); converting on the graphics queueandPyroWave compute conversion unavailable (…); converting on the graphics queueif present. - For PyroWave, include
optional codec probeerrors; for an OS/driver reset, include the matching Reliability Monitor/Windows event time and LiveKernelEvent code. A frozen picture with continuingstream timingsand no recovery warning is useful evidence too.
The picture freezes and the GPU resets during a stream #
Windows logs a LiveKernelEvent 141 (VIDEO_ENGINE_TIMEOUT_DETECTED) and the
Radeon may stay in Device Manager error Code 31 until a reboot. This is the
AMD driver's video encoder hanging inside an encode call. Rubylight's stall
watch then logs stream thread stopped making progress with
phase=encoding, while its GPU query probe still answers. No host process
can end a call blocked in the kernel driver; only Windows' timeout reset does.
If it happens on your PC:
- Reboot if the Radeon shows Code 31.
- Keep the bitrate at what the link can carry, so the client needs fewer recovery keyframes. On Wi-Fi, use AV1 when the client decodes it: it recovers from loss with a small frame instead of a keyframe.
- Try AMD's current Recommended driver and report the driver version, the
time of the
LiveKernelEvent, and the host log around it.
Playnite does not launch #
Keep a Windows user signed in, enable Playnite in Settings → Game library, and check that the game starts in Playnite locally. If Rubylight cannot find a portable copy, leave Playnite open in that user's session when starting the stream.
In Library, check Playnite's plugin status. Use Install plugin or Update plugin if offered, fully exit and reopen Playnite, then try Sync now. Use the connector bundled with Rubylight; newer Vibepollo connectors may require different host support. A Playnite CLI fallback requested warning means automatic game-exit tracking is unavailable; quit that stream manually.
Set Log level to Debug before reproducing so the log includes Playnite plugin check and the launch-pipe details. Send the failure time, installed or portable location, Desktop or Fullscreen mode, and the host log from Playnite launch prepared and Playnite plugin check through the pipe, startup, fallback or exit messages. Include any Moonlight session launch failed, Playnite library sync failed, Playnite startup failed, Playnite did not confirm startup or Playnite game exit confirmed lines. If available, include Playnite's playnite.log and the matching %APPDATA%\Sunshine\logs\sunshine_playnite-*.log.
RTSS does not start, or the game ignores the cap #
Open Maintenance → Frame limiter during the affected stream. Record Configured, Active now, RTSS detection/running state and RTSS folder. A missing tray icon alone does not establish whether the limiter is active.
In Settings → Frame limiting, check Limiter, Frame limit, Virtual display refresh and RTSS folder. None disables limiting. Physical-monitor streams need Limit every stream if you want a cap on every stream; virtual-display policy can apply its own cap. A frame limit of 0 follows the stream rate, including fractional values such as 59.94.
The folder must contain the RTSS executable and its hook library, not just a shortcut. If the log says RTSS requires administrator privileges, start RTSS as administrator before streaming or use Rubylight's installed service. If it is found and running but one game ignores the limit, check that game's RTSS profile and include the game/API in the report. If RTSS's window keeps your own limit during a stream, Rubylight could not reach the elevated RTSS: use the installed service, or run a portable copy as administrator. RTSS 7.3.7 ignores edits of its settings file and takes a new limit only through its own interface, which Rubylight uses from rc.27; the log line "RTSS frame limit applied and verified" names the RTSS version.
Rubylight restores limiter values after the last stream owning the limit disconnects. A game retaining its display must not keep the cap active. If a cap remains, check for another pending or connected stream and keep the before/during/after values with the log.
Monitors stay on, or the display layout does not return #
Check Settings → Display and app/device overrides first. Extended intentionally keeps physical displays alongside the virtual display. Exclusive and isolated layouts have different behavior. A client may also request its own virtual-display arrangement.
Check Restore displays on disconnect, Restore delay and Keep a disconnected display for against the expected lifecycle. An app or retained remote-monitor session may still own a display after video disconnects.
When all streams have ended, use Maintenance → Displays to inspect whether the current layout matches the saved one. Restore saved layout applies that saved layout; Save current layout replaces it with the arrangement you currently want. Saving, restoring and resetting are unavailable during streaming.
Disconnect virtual displays stops every stream and removes Rubylight-created virtual displays. Reset display settings memory forgets pending display changes that Rubylight would otherwise undo. Use those recovery actions deliberately after recording the problem; resetting memory is not the same as restoring a layout.
rc.10 has a brief startup guard for a reproduced case where creating a virtual display reactivated a dormant monitor. Record the client, chosen layout, active monitors and log time when reporting another case.
"Virtual display did not become active before the deadline" #
Up to rc.10, a launch failed with this error when Windows left the new virtual display switched off, typically because it recalled a layout saved for the other connected displays (two TVs in duplicate mode, for example). rc.11 switches the display on itself after a second, without changing the other displays, and logs Windows left the new virtual display switched off; switched it on beside the current displays. Update before trying workarounds such as unplugging the TVs. If a launch still fails, the error says whether Windows reported the display as connected; include that line and the log around it in the report.
The stream disconnects after a second with "os error 10035" #
video sender stopped: A non-blocking socket operation could not be completed immediately. (os error 10035) comes from rc.1, which ended the stream when the Windows send buffer was momentarily full. Since rc.2 the host waits briefly, drops only those packets and keeps streaming, as Vibepollo does; the log then shows UDP send failed; packets dropped at most every five seconds. Install the current release. Frequent drop warnings mean the network cannot carry the bitrate: lower it, or use a standard codec rather than PyroWave over Wi-Fi.
A controller or other input does nothing #
Check the stream card in the console first.
- "…its device permissions do not allow it": the device lacks that input permission. Every device paired after the first starts view-only, as in Apollo and Vibepollo. Turn on Controllers (or Touch, Pen, Mouse, Keyboard) for it under Devices, then reconnect.
- "Virtual gamepad driver unavailable": the VHF gamepad driver could not be opened. Run the installer again with the gamepad driver selected.
Steam shows two controllers #
Some Steam builds can list one VHF Xbox controller twice. Steam's SDL controller discovery races its XInput and GameInput backends for the same device. Start+Select may then open both Xbox Game Bar and Steam's keyboard. SDL has an upstream fix. Users report that the Steam beta, which includes it, lists the pad once; switch to it under Steam → Settings → Interface → Client Beta Participation. The fix will reach Steam's stable client in a later update.
Steam's logs/controller.txt shows each arrival. Rubylight no longer uses ViGEmBus, which earlier releases offered as a workaround; an installed copy can be uninstalled.
A Steam Deck has no gyro, trackpads or back grips #
The host only follows what Moonlight announces when a controller connects. With Steam Input on for Moonlight, Steam on the Deck gives Moonlight a virtual pad, which has no gyro, trackpads or back grips. On the Deck, disable Steam Input in Moonlight's controller settings in Steam, then reconnect. The stream card says this when the paired device is named Steam Deck and its controller has no gyro. In Rubylight's logs, a Deck that Moonlight sees directly connects with client_type=4.
- Steam on the host doesn't show a Steam Deck: the host attaches one only with usbip-win2 installed and, with Automatic, while Steam is running on the host when the controller connects; otherwise the log says
Steam is not runningand the Deck gets a virtual DualSense. The log lineSteam Deck controller attached through usbip-win2names the usbip-win2 port, andusbip.exe portlists it. If the stream card says the attach failed, its message is usbip.exe's own. - No trackpads with Steam Input off: Moonlight builds whose SDL predates SDL 3 do not read the Deck's trackpads, so they send none.
- Back grips do nothing on the virtual pad: choose what each grip presses under Settings → Input → Steam Deck and back grips; the virtual pads have no back buttons of their own. A Deck attached as a Steam Deck keeps its own grips, and Steam Input on the host decides what they do.
- A Steam Deck controller left behind after a crash: usbip-win2 detaches it when Rubylight's connection closes, then keeps trying to attach it again.
usbip.exe attach --stop-allstops that.
See Steam Deck.
Setup rejects the profile folder's owner #
An upgrade from 2.2.1 can fail with C:\ProgramData\Butterpollo must be owned by Administrators, SYSTEM or TrustedInstaller. Older installers set the folder's access permissions but could leave an individual user as its owner. Changing access permissions alone does not change ownership.
The fix in 2.2.6 repairs that legacy ownership and the profile permissions during a verified in-place service upgrade, keeping settings and paired devices. Credentials remain restricted to SYSTEM and Administrators. Use the 2.2.6 or later installer and upgrade in the existing installation folder.
Setup still rejects reparse points (including links and junctions) and untrusted parent folders. The repair does not apply to a fresh install or to a damaged installation whose missing or unreadable package manifest forces a full reinstall. If setup still refuses the path, keep the profile backup and include the setup log from the locations below in your report.
An update stays queued #
In Maintenance → Updates, read the current phase and any error. Installation needs the installed Windows service and one minute without streams, pending connections, remote monitors or host apps. Quit a retained Desktop/game session as well as disconnecting its video. A new connection defers the update.
Install updates automatically is opt-in. Include pre-releases controls whether candidates appear, and an update-check interval of 0 disables automatic checks and installation. Manual checks remain available. Portable builds use the release-page download.
Logs and a useful report #
Use Logs → Download log for the host log or Download support bundle for a ZIP containing logs, configuration diagnostics and an available crash dump. Review the contents before posting publicly.
| File | Default location |
|---|---|
| Installed host log | %PROGRAMDATA%\Butterpollo\config\logs\butterpollo.log |
| Installed service log | %PROGRAMDATA%\Butterpollo\config\logs\service.log |
| Portable host log | Adjacent config\logs\butterpollo.log (2.2.6 or later); otherwise %LOCALAPPDATA%\ButterpolloRust\config\logs\butterpollo.log |
| Setup log | %TEMP%\butterpollo-setup-<timestamp>.log |
A custom --config-dir changes the profile location; log_path can override the host log path. The support-bundle download is named butterpollo-support.zip.
The release includes tools\collect_environment.ps1. From PowerShell in the installed or extracted package folder, collect read-only environment evidence with:
.\tools\collect_environment.ps1 -InstallDirectory . -OutputFile "$env:TEMP\butterpollo-environment.json"
Choose a new output filename if that file already exists. The collector records Windows, GPU/driver, physical network-adapter counters, host version/hash and service state. It does not start capture, change settings or upload the report. A source checkout has the same script at rust/tests/collect_environment.ps1.
For a display-specific report, butterpollo.exe --diagnostics prints the detected displays and virtual-driver status without starting a stream. In a portable/user context, its driver-access result may differ from the installed service.
Open an issue with the exact symptom and time, host/client versions, GPU and driver, wired or Wi-Fi connection, resolution/FPS/codec/HDR settings, and the relevant log or support bundle. Include the environment report when the result seems hardware-specific.