Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
29 KiB
Frame doctor runbook
Checks and fixes for a Frame that's unreachable, crashing, or whose Steam,
SteamVR, Lepton or panels misbehave. A future scripts/frame-doctor.sh should
run the checks in section order, print OK, WARN or BROKEN for each, and apply
only the fixes marked safe. Anything marked ask needs the user's OK,
and anything marked user needs a hand on the headset.
Sources are the Frame's own journal and coredumpctl history (boots from
2026-09-25 to 2026-09-28) and this repo's docs. Each entry cites where it came
from. Dates are when a fact was seen. BUILD_IDs were 20260922.6101926 until
2026-09-26 and 20260925.6191901 after.
Never do these
modprobe -r ath12kon a wedged Wi-Fi chip. It oopsed the kernel on 2026-09-28 and caused the displays-broken, Steam-damaged boot in section 3.- Leave WoWLAN armed. The next sleep breaks Wi-Fi until reboot (section 2).
- Suspend the Frame from a script. Nothing can wake it remotely (section 6).
- Let the SteamOS health checks count up to their repair. The SteamVR one re-extracts Steam at 3 failures and tries to switch OS slots at 4 (section 4).
- Kill
gamescopeto stop a gamescope crash loop. Kill the orphaned SteamVR processes instead (section 3). - Write the sudo password to disk or logs.
- Leave
power.pauseCompositorOnStandbyorpower.turnOffScreensTimeoutchanged after testing (section 3). - Force a power-off (holding Power) or reset while Steam is extracting or
repairing. That's how files got truncated on 2026-09-28. Use an orderly
systemctl reboot/poweroffor the power menu. Holding Power is for an unresponsive Frame, with the user's involvement. - Run long diagnostics while a Steam or SteamVR restart loop is live without freezing the health-check trackers first (section 4). The repair threshold is 3 SteamVR failures, and a loop reaches it in under a minute.
0. Reaching the Frame
| Path | How | Works when |
|---|---|---|
| Tailscale | ssh frame (frame.<tailnet>.ts.net) |
Wi-Fi up, and Tailscale on the Mac and the Frame |
| LAN | ssh -o HostName=192.168.1.237 -o HostKeyAlias=frame.<tailnet>.ts.net frame, or frame.local |
Wi-Fi up. The alias avoids "Host key verification failed" |
| USB-C | Same, with HostName=10.86.200.233 |
Cable to the Mac, even with Wi-Fi dead. The Mac gets en9 "Steam Frame" 10.86.200.234/29 (networksetup -listallhardwareports) |
| ADB over USB-C | adb -s frame shell |
SSH refused, for example after Developer Mode was lost (how-the-frame-works.md, boot-loop row) |
- Asleep means off the network (verified 2026-09-27, unreachable for about
2.5 h). Every path times out and nothing remote wakes it
(section 6). user: press power.
tailscale status | grep frameon the Mac shows "offline, last seen N ago". framealias doesn't resolve. The Mac's Tailscale is off. Useframe.local(tailscale.md). Bareframedoesn't resolve on macOS. Check withdns-sd -G v4 frame.local(ssh.md).- Pairing answers
403 "please put the Steam client in pairing mode". user: Steam, then Settings → Developer → Pair new host.connect.shretries for 2 min (ssh.md). - iPhone app can't use devkit pairing. It only installs an RSA key, and Citadel signs RSA with SHA-1, which OpenSSH 9.7 rejects. Use ed25519 and password pairing instead (ssh.md, 2026-09-27).
- Locked out after
connect.sh --harden. Undo withsudo rm /etc/ssh/sshd_config.d/01-frame-keys-only.conf && sudo systemctl reload sshd(ask, over USB-C or ADB) (ssh.md). - No SSH at all (Developer Mode off). Run
scripts/serve-bootstrap.sh, and the user typescurl -fsS mac.local:8765|bashin Konsole. Stop the server afterwards, because it's plain HTTP (ssh.md). - Tailscale exposes every loopback port (8080 Steam DevTools, 5555
unauthenticated ADB, 27062, 3389) to the tailnet. Check read-only with
~/.local/bin/tailscale debug prefs | grep ShieldsUp(the CLI isn't onPATH; verified 2026-09-28) and the tailnet ACLs. Report it as a WARN.~/.local/bin/tailscale set --shields-upis a mitigation, not a check, and it also blocks inbound SSH over Tailscale, so it's ask, and only with another way in available (tailscale.md). - sudo:
printf '%s\n' "$PW" | ssh frame 'sudo -S -p "" …'. It's the password the user set on the Frame.
1. Boot and crash history
ssh frame 'uptime; journalctl --list-boots --no-pager | tail -n 6'
ssh frame 'journalctl -b -1 -k --no-pager -q | grep -aE "Unable to handle kernel|Internal error|Kernel panic" | tail -n 3'
ssh frame 'coredumpctl list --no-pager --since -1d'
- Kernel oops in the previous boot. Report "oops observed". An oops alone
doesn't prove a reset, because Linux can keep running after one. Classify the
reset as unclean only if the oops is among the last lines of that boot and no
shutdown lines follow
(
journalctl -b -1 -q -n 30 | grep -aE "systemd-shutdown|Reached target.*(Reboot|Power)"is empty). On 2026-09-28 the oops was the last thing logged at 20:57:53. After an unclean reset, check sections 3 and 4 closely. - A boot ending with no shutdown lines and no errors. On 2026-09-26 there were five boots of 0–12 min like this (−12, −9, −8, −7, −5), with nothing failing beforehand. They were probably hard power-offs during setup. Treat them as unexplained, not as crashes.
- Crash signatures seen so far (all
coredumpctl, UID 1000):
| When | What crashed | Cause | Section |
|---|---|---|---|
| 09-25 21:02–21:03 | vrcompositor SEGV, steamwebhelper SEGV, then Android composer, surfaceflinger and gamescope ABRT | Lepton crash cascade. Two pasta processes were both failing to listen on port 16385 just before |
7 |
| 09-25 22:30–22:42 | app_process64 ×3 |
Android apps during APK testing | 7 |
| 09-25 23:20 | ffmpeg |
hardware H.264 encoder | 10 |
| 09-26 13:56–22:45, 09-27 09:51 | chromium-xr/chrome ×16 |
Chromium XR (panels, Mac view) | 8 |
| 09-26 21:11–21:49 | XRService ABRT ×9, vrcompositor SEGV ×5, gamescope ABRT ×4 | leftover SteamVR processes from a failed start (29 Steam restarts, 31 SteamVR failures that boot) | 3 |
| 09-28 16:27–17:49 | app_process64 ×4 |
Android runtime amid binder_user_error floods |
7 |
| 09-28 17:01 | kdeconnectd |
SMS plugin during device teardown | 9 |
| 09-28 20:58–21:39 | vrcompositor SEGV ×10, XRService ×15, steamwebhelper ×2 | broken displays after a kernel oops | 3 |
Per-boot counters a doctor should print:
ssh frame 'for b in 0 -1; do
k=$(journalctl -b $b -k -q) || { echo "boot $b: journal unreadable"; continue; }
u=$(journalctl -b $b --user -u steam.service -q) || { echo "boot $b: user journal unreadable"; continue; }
s=$(journalctl -b $b -q) || { echo "boot $b: journal unreadable"; continue; }
echo "boot $b dsi=$(grep -ac "wait for video done" <<<"$k") steam_restarts=$(grep -ac "Scheduled restart" <<<"$u") steamvr_fail=$(grep -ac "steamvr.service: Failed" <<<"$s")"
done'
Report an unreadable journal as unknown, not as zero. What matters is whether the counts are still rising, so run it twice a minute apart. One or two SteamVR start failures around boot are normal (how-the-frame-works.md, boot-loop row). Healthy boots on 2026-09-28 were 0 / 0 / 0. The 2026-09-26 21:22 boot reached 0 / 29 / 31 (leftover processes), and the 2026-09-28 20:58 boot reached 424 / 61 / 62 (broken displays), both rising every ~15 s.
2. Wi-Fi
| Check | Healthy | Broken |
|---|---|---|
nmcli -t d | grep ^wlan0 |
wlan0:wifi:connected:… |
wlan0:wifi:unavailable: |
journalctl -b -k | grep -a ath12k |
none, or a few at boot | failed to wakeup from wow: -110, Resuming from non M3 state (RESET), WMI_PDEV_SET_PARAM_CMDID timeout, fail to start mac operations |
iw phy phy0 wowlan show |
WoWLAN is disabled. |
wake up on magic packet |
- WoWLAN armed (safe). Disarm it by UUID, because the user may have made
same-name duplicates.
defaultmeans "use NetworkManager's globalwifi.wake-on-wlan". The Frame sets none (checked 2026-09-28), so it falls back toignore, which leaves the chip untouched and doesn't clear an armed chip.0disarms it:Use the active connection on wlan0, because there can be same-name duplicates.set -e U=$(nmcli -t -f UUID,DEVICE c show --active | awk -F: '$2=="wlan0"{print $1}') [ -n "$U" ] || { echo "no active connection on wlan0"; exit 1; } nmcli -g 802-11-wireless.wake-on-wlan c show "$U" # record the old value systemd-run --user --wait --pipe -q nmcli c modify "$U" 802-11-wireless.wake-on-wlan 0 systemd-run --user --wait --pipe -q nmcli device modify wlan0 802-11-wireless.wake-on-wlan 0 iw phy phy0 wowlan show | grep -q "WoWLAN is disabled" || { echo "still armed"; exit 1; }c modifysaves the setting.device modifychanges only WoWLAN on the live device, unlikedevice reapply, which would also apply any other saved changes such as IP or DNS. Tested 2026-09-28: Wi-Fi stayed connected. NetworkManager only allows the modify undersystemd-run --user. From SSH it'sauth. Leave the profile at0, since that stays safe even if a globalwifi.wake-on-wlanis added later. Setting the recorded old value back is ask. On 2026-09-28 the profile was set back todefaultby hand. If the Wi-Fi isunavailable, there's no active connection, so this has to wait for the reboot, and then arming comes from the profile. unavailableafter resume (user/ask). Do a clean reboot: power menu, orsudo systemctl rebootover USB-C. Never reload the module.- Duplicate "ThisIsTheWifi" profiles. Ones with
TIMESTAMP-REALneverare unused. Deleting them is ask.
3. Displays, SteamVR, gamescope
| Check | Healthy | Broken |
|---|---|---|
journalctl -b -k | grep -ac "wait for video done" |
0 |
hundreds (msm_dsi ae94000.dsi / ae96000.dsi) |
coredumpctl list vrcompositor --since -10min |
none | SEGV every ~15 s |
grep -a "failed to wait for present" ~/.local/share/Steam/logs/vrcompositor.txt |
none recent | WaitForPendingPresent: failed to wait for present |
journalctl -b --user -u steamvr.service | grep -a "left-over process" |
none | Found left-over process … (vrserver) … (vrcompositor) in control group |
journal gamescope |
quiet | rendervulkan.cpp:2181 … Assertion '!modifiers.empty()' about once a second |
-
Broken displays (DSI timeouts). The chain is: the GPU can't present, vrcompositor SEGVs on its first frame,
steamvr.servicefails and stops the gamescope VR session, and gamescope and Steam get SIGKILLed. The user sees "There was an issue launching Steam". Fix (ask/user): a clean reboot. An unclean reset after a kernel oops caused it, and the clean reboot had 0 DSI errors (2026-09-28). Don't touch Steam while this is happening. -
Leftover SteamVR processes (2026-09-26 21:22 boot). A first
steamvr.servicestart failed ondependency, its vrserver, XRService and vrcompositor kept running, and each restart crashed against them. The same fix as the next item applies, with the same guard. -
gamescope crash loop on
!modifiers.empty()(verified 2026-09-25, apks.md). gamescope keeps attaching to SteamVR processes orphaned from a dead session. The broad fix isfor p in vrdashboard vrcompositor vrserver; do pkill -TERM -x $p; done, and it recovers within about a minute. That kills every matching process, including a working session, so it's always ask. A doctor may signal automatically only individually verified stale PIDs, and only when all of these hold:- The loop is live: new vrcompositor/gamescope crashes in the last 2
minutes, and
NRestartsrising between two reads. - The process started before the current
steamvr.servicemain process: compareps -o pid,lstart,args -C vrserver,vrcompositor,vrdashboardwithsystemctl --user show steamvr.service -p ExecMainStartTimestamp. - The journal ties it to the failed run:
Found left-over process <pid> (…) in control group.
Re-read
/proc/<pid>/statstart time andcommjust before signalling, and signal by number, never by name. A process that's merely outsidesteamvr.service's cgroup could be a legitimate launch, so that's ask. - The loop is live: new vrcompositor/gamescope crashes in the last 2
minutes, and
-
Standby test settings left on. Check that
vrcmd --get-settings(or~/.config/openvr/config/steamvr.vrsettings) showspower.pauseCompositorOnStandby= 1 andpower.turnOffScreensTimeout= 5. If they differ, report a WARN and leave them alone (ask), since the user may want them. If a doctor run changes them for a test, it must snapshot both values first, including whether they were set insteamvr.vrsettingsat all. Afterwards it restores exactly those values, removing the keys if they were absent, rather than the defaults 1 and 5. The bool setter needs1/0, nottrue(how-the-frame-works.md). -
Dashboard open over an app (
visible-blurredjust means it's open). RunSteamClient.OpenVR.VROverlay.HideDashboard()over CDP on port 8080 only when the doctor itself is driving an app test. Otherwise it's ask, because the user may have opened it. -
Steam launch stuck in standby at
ShowInterstitials/CreatingProcess(console_log.txt). RunSteamClient.Apps.ContinueGameAction(<action id>, "<appid>", "<task>")over CDP.
4. Steam client and the SteamOS health checks
| Check | Healthy | Broken |
|---|---|---|
systemctl --user show steam.service -p NRestarts |
0 or stable |
climbing every ~15 s |
tail -n 40 ~/.local/share/Steam/logs/connection_log.txt | grep -a "Logged On" |
[Logged On, …] [U:1:<id>] |
only [Logged Off, 0, 0] [U:1:0] |
grep -a BVerifyInstalledFiles ~/.local/share/Steam/logs/steam_output.log |
none | <file> is N bytes, expected M, bad symlink … |
last line of steam_output.log |
client running | Installing update... or Extracting package... for minutes |
pgrep -af child-update-ui + /proc/<pid>/wchan |
none | drm_syncobj_array_wait_timeout |
cat /run/user/1000/steam{,vr}-short-session-tracker; ls -l those files |
empty | frog… / frog:glasses:… building up |
Check section 3 first. If the displays are broken, Steam can't get past its first frame, whatever the files look like.
The two health checks (read from /usr/share/deckard/, 2026-09-28):
steam-health-check(run bysteam.service) appendsfrogtosteam-short-session-trackerfor each run that fails in under 120 s or lasts under 5 s. At 5 it runsdo_repair. On BUILD_ID 20260925.6191901 the script deletes~/.steam(keepingregistry.vdf) and then either extracts/usr/lib/steam/steam.tar.zst(705 MB) over~/.local/share/Steam(the "unpacked" install this Frame has) or, on an overlay install, deletes the upper-dir files that shadow/usr/local/steam. Then it touches.install-complete. It also repairs at every Steam start if.install-completeis missing, whatever the counter says.steamvr-health-check(run bysteamvr.service) appendsfrog:glasses:for each failed or under-10-s SteamVR run. At 3 it runssteam-health-check --repair-now. At 4 it also runssteamos-bootconf set-mode reboot-other, which fails as non-root.- What a repair erases isn't consistent across notes. On 2026-09-26
(BUILD_ID 20260922.6101926) the boot-loop row in
how-the-frame-works.md records that all of
~/.local/share/Steamwas deleted, including games, login and Developer Mode. On 2026-09-28 the scripts above only extract over it, a repair ran at 21:13 (.install-completemtime), and the login survived. Treat any repair as possibly destructive. Before a restart that could trigger one, check that.install-completeexists. - Stop them counting while you fix the cause (safe, resets at boot). Do
this first, right after connecting, if
NRestartsor either tracker is rising, before any long checks:A doctor must stop and not restart Steam or SteamVR unless this exits 0. It's idempotent, so run it on files that are already 444. Both were already 444 on 2026-09-28, applied by an earlier session. This only stops the counting. The start-time repair whenrc=0 for f in /run/user/1000/steam-short-session-tracker /run/user/1000/steamvr-short-session-tracker; do { [ -e "$f" ] || : > "$f"; } && chmod u+w "$f" && : > "$f" && chmod 444 "$f" || rc=1 # verify: empty and not writable [ -e "$f" ] && [ ! -s "$f" ] && [ ! -w "$f" ] && echo "frozen $f" || { echo "NOT frozen $f"; rc=1; } done exit $rc.install-completeis missing still runs. Re-apply after every reboot while the loop's cause is unfixed.
Fixes:
-
Verify files yourself (safe, read-only). The record is
~/.local/share/Steam/package/steam_client_<branch>_linuxarm64.installed, with lines ofpath,size;mtime;crc32(size-1is a directory). Compare sizes andzlib.crc32(13,518 files on 2026-09-28).steam_output.logis rewritten on every launch, so copy it before the next restart.bad symlinkreports taken mid-extraction are transient. -
Truncated files (ask). Restart Steam (
systemctl --user restart steam.service), and it re-verifies and re-extracts frompackage/. Preconditions:- The displays are healthy.
- The trackers are frozen.
.install-completeexists.- The updater is idle: the
steam_output.logtail hasn't changed for 60 s and the steam process isn't writing (/proc/<pid>/iowrite_bytesis steady). - No game or app is running.
Re-verify afterwards.
-
Updater deadlocked on the update UI. Kill only the
-child-update-uiprocess, and the install continues (worked 2026-09-26). On 2026-09-28 it was followed by a truncatedsteamui.so, so re-verify afterwards. If the deadlock came from broken displays, fix those first. -
Stale pending install (ask, with backup).
package/steam_client_<branch>_linuxarm64with no extension means an install is pending. Only act when all of these hold:cmpshows it's identical to.manifest.- The verify is clean.
- The updater is idle (as above).
Then stop Steam, move it to
~/.cache/frame-control/…pending-backup, and start Steam. The log should showNothing to do, thenVerification complete, then webhelpers. -
Heavy repair (ask).
steam-health-check --repair-now, or the boot menu'sRepair Steam Installation(section 12). -
Dead ends (don't repeat).
STEAM_EXTRA_ARGS=-no-child-update-uistill draws GLX in-process and blocks.- With
DISPLAYunset, Steam exits ("XOpenDisplay failed"), with no text fallback. - Xvfb has no GLX visual here.
-
Steam's launch path is
steam.service→/usr/share/deckard/select_steam.sh RUNSTEAM.sh. Runtime drop-ins in/run/user/1000/systemd/user/steam.service.d/are cleared at reboot. Remove any you add. -
create-shortcutrefuses ids with hyphens (missing/invalid arguments). Ids must match^[A-Za-z_][A-Za-z0-9_.]+$. It reports "Steam client is not running" when Steam is down, and re-running finishes the install without a re-upload (sideloading.md).
5. Idle sleep and keep-awake
- Frame slept mid-task. SSH doesn't count as activity, and Steam's idle
timer (
system_idle_suspend_ac_sec3600,…_battery_sec900) suspends it. The journal showsSwitching to power state: [ k_ESystemPowerState_Sleep ]. Fix (safe):scripts/keep-awake.sh onbefore long work andoffafter. It uses one shared unit and one saved-settings file. So a doctor records whetherfc-keep-awakewas already active, and runsoffonly if it was the one that turned it on. Otherwise it releases another task's lock and restores that task's saved timers. It sets both timers to 0 and holds thefc-keep-awakeuser-unit inhibitor. A plain SSH-session inhibitor is refused. - Check:
systemctl --user is-active fc-keep-awake,systemd-inhibit --list | grep "Frame Control". WARN if it's held with no agent working, since that drains the battery on battery power. - Charging shows "Discharging" at ~0 W while full on a charger. This is a reporting quirk. Treat under 0.5 W on a charger as "not charging" (how-the-frame-works.md).
6. Remote wake
It doesn't work on this build. WoWLAN arms, but the WCN7850 is reset in both
deep and s2idle, packets don't wake it, and Wi-Fi is dead after resume.
Tested 2026-09-28. Details are in how-the-frame-works.md
and open-questions.md. Doctor checks: cat /sys/power/mem_sleep should read s2idle [deep] (resets at boot), and WoWLAN
should be disabled.
7. Lepton (Android)
| Check | Broken sign |
|---|---|
podman ps --format '{{.Names}} {{.Ports}}' |
two containers with the same name or instance, or both bound to the same host port. Several instances with different ports are normal (apks.md) |
journal pasta |
Listen failed for HOST TCP port 0.0.0.0/16385: Address already in use repeating |
| journal | android.hardware.graphics.composer@2.1-service or surfaceflinger aborts right after an app crash |
dmesg / journal |
floods of binder_user_error: N callbacks suppressed near app_process64 crashes |
| journal | Clearing baked app data due to non steamlaunch container |
- Duplicate Lepton containers or port clash (2026-09-25 21:01). Two
pastainstances fought over 16385, and a minute later the compositor, webhelper, Android composer, surfaceflinger and gamescope crashed together. Fix (ask): find the two instances that share the port (podman ps,ss -ltnp | grep 16385) and stop only the duplicate. Leave other instances running. - Lepton's graphics HAL aborts after an app crash, taking the container down (3 times on 2026-09-25). It's intermittent, so retry (apks.md). Then check section 3 for a gamescope loop.
- All ADB-installed apps gone. Lepton Development wipes its data whenever
it exits outside a Steam launch. Use
install-apk.sh(a per-app Steam launch, whose data survives), or the launch optionLEPTON_NO_CLEANUP=1 %command%(inferred) (apks.md). - App dies on first file write with
ENOENT. ItsSTEAM_COMPAT_DATA_PATHis outside~/.local/share/Steam. Usesteamapps/compatdata/<id>. - Lepton won't start outside Steam. Set
IS_PARENT=trueand usesetsid --wait. "unbound variable" meansSTEAM_COMPAT_SHADER_PATHis unset (apks.md). - App compatibility, not a fault (apks.md):
- Compose older than 1.11, SDL2/Kivy and Godot 4.3 crash on the missing clipboard service.
INSTALL_FAILED_OLDER_SDKmeans minSdk is over 30.INSTALL_FAILED_NO_MATCHING_ABISmeans there's no arm64 build.monkeyreturning-5means you should launch the activity directly.--briefprints a metadata line first, so take the last line:adb -s $S shell am start -W -n "$(adb -s $S shell cmd package resolve-activity --brief -c android.intent.category.LAUNCHER <pkg> | tail -n 1)".
8. Chromium XR (panels, Mac view, WebXR)
- 16 crashes on 2026-09-26/27. The logs showed
Failed to create a temporary file for memory-mapping: No such process (3), thenReceived signal 11 SEGV_MAPERR. The cause isn't known yet. Check withcoredumpctl list chrome --since -1d. - Zygote crash about 30 s after a Steam-launched start. Steam's
gameoverlayrenderer.sois inLD_PRELOAD, and the launcher strips it (verified 2026-09-27, webxr-chromium.md). Check that the running chrome's/proc/<pid>/environhas nogameoverlayrenderer. - XR process seccomp crash (syscall 209) or
VRInitError_Init_Internal. The launcher runs with--disable-seccomp-filter-sandbox. Use that profile only for VR sites (webxr-chromium.md). - Mac view kept working when Steam was down (2026-09-28). It's a separate Chromium talking to the Mac over the LAN. It's a useful way in when Steam is broken, but it's killed by any reboot and needs relaunching.
9. KDE Connect
kdeconnectdcrashed on 2026-09-28 17:01 inkdeconnect_sms.sounderDevice::~Device(device teardown). Check whether it's still running withpgrep -f frame-control/kdeconnect/root/usr/lib/kdeconnectd. Fix (safe): restart it the way this repo launches it. A doctor should report a missing daemon rather than guess.
10. Streaming and capture
ffmpegcrash withh264_v4l2m2m(hardware encoder, 2026-09-25/26). Uselibx264 -preset ultrafast -tune zerolatency(how-the-frame-works.md).vrcmd --screenshotwrites nothing. Useui/frame_vrshot.py(IVRScreenshots).- No Mac cursor in the VNC mirror. Load
scripts/mac-cursor-ring.luain Hammerspoon on the Mac (dofile(".../scripts/mac-cursor-ring.lua")in~/.hammerspoon/init.lua). It isn't a standalone script. Toggle it with ctrl+alt+cmd+M. - Remmina asks for the Mac login password. macOS offers RFB type 30
first. Seed the password with
--update-profile … --set-option password(streaming.md).
11. Panels
- Window stays on the default panel. Run one
panel-on-frame.shat a time. Tag windows by hand withDISPLAY=:0 xprop -id <win> -f STEAM_GAME 32c -set STEAM_GAME <id>(panels.md). - Single-instance apps (Remmina, KDE). Close them in Plasma first.
- Wayland-only apps can't be floated this way.
- Dragging selects instead of scrolling. That's by design for tagged windows (laser mode).
Failed to get app infofor a made-up id is benign.
12. Boot loop, recovery, re-image
Work down this list, least destructive first (recovery-and-images.md). The boot menu is inferred from Valve's docs and hasn't been tried on this Frame.
- If the Frame is reachable, freeze the health-check trackers first and verify them (section 4), then diagnose. A reboot clears the freeze and restarts the failing services, and 3 SteamVR failures trigger a repair.
- Clean reboot only when the diagnosed fault needs one (broken displays, dead Wi-Fi). Straight after reconnecting, freeze and verify the trackers again before anything else.
- Boot menu (user): shut down cleanly if the Frame responds. Hold Power
~10 s until the LED is off only if it doesn't, and never while Steam is
extracting or repairing. Then power on holding AUX (top button). Choose
Previous(the other A/B slot, keeps data), then tryRepair Steam Installation. Erase User Datawipes~: SSH keys, Tailscale, Flatpaks and setup (ask).- Re-image with
steamframe-oobe-repair-<build>over USB or cable/EDL (qdl). Copies are in~/Downloads/steam-frame-recovery/(ask).
What a doctor script should do
- Find a path (Tailscale, then LAN, then USB), and report which one worked.
- Print uptime, the last boots, and whether the previous boot ended in an oops.
- Run the read-only checks in sections 2–11 and print one line each: OK, WARN or BROKEN.
- Order matters. If a restart loop is live (
NRestartsor a tracker rising between two reads a few seconds apart), freeze the trackers before any other check. Then fix displays before Steam. After any reboot, check the trackers again, because they reset. - Apply only safe fixes, printing each command. For reboots, deleting profiles, heavy repairs and anything touching a user profile: print the fix and ask.
- Never do anything under "Never do these".
- Re-run the checks after any fix and report the before and after.
- The fake-Frame harness (
fakeframe-ctl sleep|disk-full|sshd|devkit-service|keys, testing.md) can exercise the unreachable, disk-full and no-SSH branches without a headset.
Incident 2026-09-28
| Time | What happened |
|---|---|
| 19:36 | WoWLAN armed, deep suspend, magic packets sent. No wake. Power-button resume: chip in MHI RESET, Wi-Fi dead. User restarted. |
| 20:10 | WoWLAN disarmed. Clean boot, no DSI errors. |
| 20:29 | s2idle via sudo, WoWLAN re-armed, suspend. No wake, same chip reset, Wi-Fi unavailable. |
| 20:57:53 | Over USB-C: modprobe -r ath12k, and the kernel oopsed and the Frame reset itself. |
| 20:58 | Boot with steamclient.so truncated (18.6 of 50.3 MB) and DSI timeouts from +28 s. Steam "couldn't connect", then "There was an issue launching Steam". Mac view still worked. |
| 21:00–21:13 | Steam re-extracts and hangs on "Installing update..." (update UI stuck on the GPU). Killing the UI child let it continue, and steamui.so was later found truncated. The health-check repair ran at 21:13. |
| 21:28 | Own CRC check: all 13,518 files OK. |
| 21:30 | Moved aside the identical pending manifest. Steam reached login, then died every ~15 s: vrcompositor SEGV on DSI timeouts. |
| 21:39 | User did a clean reboot. 0 DSI errors, Steam logged on 21:40:30, NRestarts 0. |