Known Issues
Documented bugs with confirmed root causes, recovery procedures, and permanent fix references. Each entry links to a tracking issue.
Kiosk overlay opens on VDD, blocking Mercator from rendering on streamed display
Issue: #373 Observed on: fluffy-dumpling-87 (node-11da9ea3, bxl1-test-2, cloud-seven), 2026-05-24
Symptom: iPad stream shows the kiosk overlay (branded Hydra backdrop) while stream_status=streaming and GPU utilization on the body is high. A screenshot of the VDD shows the full-screen kiosk overlay instead of the experience. Mercator is running but rendering on the physical dongle display (DISPLAY1, offset 1080,0) outside the Sunshine capture area.
Root cause: On VDD-enabled bodies, the primary display (offset 0,0) is the virtual display that Sunshine captures. The physical dongle display sits at offset 1080,0. kioskoverlay.exe opens on the primary display (the VDD), occupying it as a fullscreen borderless window. When Unreal Engine starts in borderless-windowed mode (FullscreenMode=1), Windows steers it away from the already-occupied primary and places it on the secondary dongle display. Sunshine captures only the VDD, so only the kiosk overlay is delivered to the stream.
Diagnosis:
- Check
stream_status=streamingand GPU utilization is high on the body - Take a screenshot:
GET http://localhost:47991/api/v1/debug/screenshot— if it shows the kiosk overlay full-screen, this is the cause - Check display layout via hydracluster exec:
Get-DisplayInfoor inspectVirtualDisplayEnabledin the body config — VDD as primary at offset 0,0, dongle at 1080,0
Recovery:
# Disable the kiosk overlay via hydracluster exec — stream stays live, no reconnect needed
Invoke-RestMethod -Uri "http://localhost:47991/api/v1/kiosk/disable" -Method POST -Headers @{Authorization="Bearer <body-token>"} -UseBasicParsing
Mercator moves to the VDD immediately. The stream stays live throughout — no force-stop, no reconnect required.
Warning: Do not re-enable the kiosk overlay (POST /api/v1/kiosk/enable) while a VDD stream is active. kioskoverlay.exe will open on the VDD again and push Mercator back to the dongle display, reproducing the bug immediately. Kiosk mode on VDD-enabled bodies must remain disabled until the permanent fix is in place.
If Mercator had previously saved the dongle position in GameUserSettings.ini, also follow the recovery in issue #372 before the next stream attempt (not needed for the current live stream).
Permanent fix: In hydrabody, when VirtualDisplayEnabled is true, kioskoverlay.exe must be launched targeting the non-primary (dongle) display rather than the primary VDD. The overlay should move to the dongle display so the VDD remains free for Sunshine to capture the UE output.
GameUserSettings.ini recovery after Mercator opens on wrong display
Issue: #372 Observed on: fluffy-dumpling-87 (bxl1-test-2, cloud-seven), 2026-05-24
Note: This entry covers the manual recovery step (deleting GameUserSettings.ini) for when Mercator has previously opened on the wrong display and saved that position. The root cause of Mercator landing on the wrong display in the first place is the kiosk overlay bug tracked in issue #373 — fix that first to prevent recurrence.
Symptom: iPad stream shows the kiosk overlay (branded Hydra backdrop) while stream_status=streaming and GPU utilization on the body is high. Mercator processes are running and consuming full GPU, but the experience never appears. UE has opened its window on a secondary physical display outside the Sunshine capture area, and saved that position to GameUserSettings.ini.
Root cause: Once UE has opened on the wrong display (triggered by the kiosk overlay bug, see #373), it saves the window position to GameUserSettings.ini. On subsequent stream attempts, UE re-uses that saved position even after the display layout changes, rendering on the dongle display outside the Sunshine capture area. Add -WinX=0 -WinY=0 to the UE launch args as a permanent mitigation (see permanent fix below), but the ini file must also be cleared once it contains a bad position.
Diagnosis:
- Check
stream_status=streamingand GPU utilization is high on the body GET http://localhost:47991/api/v1/debug/screenshoton the body (requires bearer token or WireGuard network) — if it shows the kiosk overlay, this is the cause- Confirm via
Get-Process | Where-Object {$_.Name -match "Mercator"}— both Mercator56 and Mercator56-Win64-Shipping present with high CPU
Recovery:
# Step 1: delete the saved window position (via hydracluster exec on the body node)
Remove-Item "C:\Users\hydra\AppData\Local\Mercator56\Saved\Config\Windows\GameUserSettings.ini" -Force -ErrorAction SilentlyContinue
# Step 2: force-stop the stream via hydrabody HTTP API
Invoke-RestMethod -Uri "http://localhost:47991/api/v1/stream/stop" -Method POST -UseBasicParsing
# Step 3: verify clean state
Invoke-RestMethod -Uri "http://localhost:47991/api/v1/stream/sessions" -UseBasicParsing | ConvertTo-Json
# Expect: status=idle, sessions=null
After verifying idle, reconnect the head — UE will default to the primary display (VDD).
Permanent fix: Add -WinX=0 -WinY=0 to the UE launch args in hydrabody pkg/provider/experiences.go lines 48–54 (streamResolution function). This pins the window to (0,0) regardless of any saved position in GameUserSettings.ini. Also fix the root cause (issue #373): the kiosk overlay must open on the non-primary dongle display so it never steers UE away from the VDD.
ForceStop HTTP 400 errors in body logs
Issue: #367
Symptom: Body logs show [stream] ForceStop failed (close running app: HTTP 400: ...). Stream does terminate but via fallback process kill.
Root cause: hydrabody does not send Content-Type: application/json to Sunshine's CloseRunningApp endpoint. Sunshine rejects the request with 400. The fallback kill-by-name succeeds.
Impact: Low — streams do terminate cleanly via the fallback. No action needed.
iPad pairing returns "Pairing OK — cert 0 bytes" and stream fails
Issue: #371
Symptom: iPad pairing log shows Pairing OK — cert 0 bytes; stream fails immediately after with no user-visible error.
Root cause: Two interacting bugs:
- Bug A (hydra-moonlight-ios
HydraPairSession.m): When Sunshine returns "already paired", the unpair request uses a hardcoded UUID (0123456789ABCDEF) instead of the real device UUID fromIdManager.getUniqueId(). Sunshine silently ignores the unpair (wrong UUID), the retry pair also returns "already paired", and the code returns an empty cert (0 bytes).AppState.swiftlogs "Pairing OK — cert 0 bytes" and proceeds withnil serverCert, causing stream failure. - Bug B (hydraheadflatscreen cross-district pairing): A kiosk head in one district can flood a body in another district with pairing requests, accumulating 250+ named_device entries in
sunshine_state.json(observed: 324 KB), triggering Bug A.
Recovery:
- Stop the cross-district kiosk stream
- Restart SunshineService on the affected body
- Clear
sunshine_state.jsonon the body (back it up first)
iPad can pair successfully once the state is clean.