A per-user Playnite install is invisible to a SYSTEM host, and one tile killed the whole library #225

Merged
enricobuehler merged 1 commits from fix/playnite-launcher-resolve into main 2026-08-14 16:07:40 +00:00
Owner

Field report: syncing the Playnite plugin failed outright, with no games at all reaching the library.

PUT /library/provider/playnite failed: entries[9]: launch.value for kind
launcher_ui names a launcher this host cannot open (playnite)

Two defects. The second is why it cost the entire library rather than one tile — the same shape as the unservable-cover bug sanitize_art_paths was introduced to fix (#215), on the launch side this time.

1 · The host looked in the wrong hive, and for the wrong key

playnite_fullscreen_exe() read HKEY_CURRENT_USER, then fell back to %LOCALAPPDATA%. But the Windows host is a LocalSystem SCM service, so:

  • its HKEY_CURRENT_USER is the SYSTEM hive (S-1-5-18), never the person's;
  • its %LOCALAPPDATA% is C:\Windows\System32\config\systemprofile\AppData\Local;
  • HKLM only hits a machine-wide install.

Playnite installs per-user by default, so on a default install every one of those misses. The doc comment reasoned correctly that Playnite is per-user and then read the one HKCU that cannot see it.

It also hardcoded …\Uninstall\Playnite. Playnite ships an Inno Setup installer, and Inno registers <AppId>_is1 — measured on the .133 box, where Git and Inno itself appear as Git_is1 and Inno Setup 6_is1. That key matched nothing anywhere.

Now: every loaded hive under HKEY_USERS (only logged-on users' hives are loaded, which is exactly the set that can be streaming — the same trade-off procscan::steam_running_hint makes) plus both HKLM views, matched on DisplayName rather than key name, then C:\Users\*\AppData\Local\Playnite for the conventional install and for a user whose hive is not currently loaded.

2 · One unopenable tile 400'd the whole reconcile

validate_provider_payload returned Err on the first offending entry. The Playnite plugin appends a single launcher tile beside its games, so refusing the payload cost the operator every game in it — and the plugin only ever sees HostRequestError, so it cannot say which entry or that the rest were fine.

The design error was valid_launcher_ui conflating two different failures. Split:

  • known_launcher_ui — vocabulary. A value outside the platform's set is a plugin bug that no amount of installing things will fix, so it stays a hard 400; the author has no other way to find out.
  • resolvable_launcher_ui — environment. A known value that will not resolve just means the launcher is not installed here, which is an ordinary fact about the box.

sanitize_launcher_entries drops only the latter, with one aggregated warn, and the games sync.

Verification

All #[cfg(windows)], and the host does not build on macOS at all — so this was verified on the .133 Windows box (16 pre-existing macOS build errors, identical count with and without this change; none in the touched files).

  • cargo check -p punktfunk-host --testsexit 0, no warnings
  • cargo test -p punktfunk-host library::59 passed, 0 failed, including the new an_unopenable_launcher_tile_costs_only_itself
  • Two-sided resolver probe (throwaway module in the box's scratch tree, never committed):
    • no Playnite → candidates enumerated from the profile scan, RESOLVED: None
    • a fake install planted at C:\Users\Administrator\AppData\Local\Playnite\Playnite.FullscreenApp.exeRESOLVED: Some(…), i.e. found through exactly the path the old code could never reach as SYSTEM
    • with it resolving, playnite_launcher_opens_the_fullscreen_app runs its full body instead of early-returning, and passes (FullscreenApp not DesktopApp, no playnite:// URI, working dir = the exe's parent)
    • planted install removed afterwards

Note

Until this ships, the plugin-side escape hatch is "launcher": false in <config_dir>/plugin-state/playnite/config.json. That was config-file-only and invisible in the UI; unom/punktfunk-plugin-playnite#5 surfaces it in Settings.

Field report: syncing the Playnite plugin failed outright, with **no games at all** reaching the library. ``` PUT /library/provider/playnite failed: entries[9]: launch.value for kind launcher_ui names a launcher this host cannot open (playnite) ``` Two defects. The second is why it cost the entire library rather than one tile — the same shape as the unservable-cover bug `sanitize_art_paths` was introduced to fix (#215), on the launch side this time. ## 1 · The host looked in the wrong hive, and for the wrong key `playnite_fullscreen_exe()` read `HKEY_CURRENT_USER`, then fell back to `%LOCALAPPDATA%`. But the Windows host is a **LocalSystem SCM service**, so: - its `HKEY_CURRENT_USER` is the **SYSTEM hive** (`S-1-5-18`), never the person's; - its `%LOCALAPPDATA%` is `C:\Windows\System32\config\systemprofile\AppData\Local`; - `HKLM` only hits a machine-wide install. Playnite installs **per-user by default**, so on a default install every one of those misses. The doc comment reasoned correctly that Playnite is per-user and then read the one `HKCU` that cannot see it. It also hardcoded `…\Uninstall\Playnite`. Playnite ships an **Inno Setup** installer, and Inno registers `<AppId>_is1` — measured on the .133 box, where Git and Inno itself appear as `Git_is1` and `Inno Setup 6_is1`. That key matched nothing anywhere. Now: every **loaded** hive under `HKEY_USERS` (only logged-on users' hives are loaded, which is exactly the set that can be streaming — the same trade-off `procscan::steam_running_hint` makes) plus both `HKLM` views, matched on **`DisplayName`** rather than key name, then `C:\Users\*\AppData\Local\Playnite` for the conventional install and for a user whose hive is not currently loaded. ## 2 · One unopenable tile 400'd the whole reconcile `validate_provider_payload` returned `Err` on the first offending entry. The Playnite plugin appends a single launcher tile beside its games, so refusing the payload cost the operator every game in it — and the plugin only ever sees `HostRequestError`, so it cannot say which entry or that the rest were fine. The design error was `valid_launcher_ui` conflating two different failures. Split: - **`known_launcher_ui`** — vocabulary. A value outside the platform's set is a plugin bug that no amount of installing things will fix, so it stays a hard 400; the author has no other way to find out. - **`resolvable_launcher_ui`** — environment. A known value that will not resolve just means the launcher is not installed here, which is an ordinary fact about the box. `sanitize_launcher_entries` drops only the latter, with one aggregated warn, and the games sync. ## Verification All `#[cfg(windows)]`, and the host does not build on macOS at all — so this was verified on the **.133 Windows box** (16 pre-existing macOS build errors, identical count with and without this change; none in the touched files). - `cargo check -p punktfunk-host --tests` → **exit 0**, no warnings - `cargo test -p punktfunk-host library::` → **59 passed, 0 failed**, including the new `an_unopenable_launcher_tile_costs_only_itself` - **Two-sided resolver probe** (throwaway module in the box's scratch tree, never committed): - no Playnite → candidates enumerated from the profile scan, `RESOLVED: None` - a fake install planted at `C:\Users\Administrator\AppData\Local\Playnite\Playnite.FullscreenApp.exe` → `RESOLVED: Some(…)`, i.e. found through exactly the path the old code could never reach as SYSTEM - with it resolving, `playnite_launcher_opens_the_fullscreen_app` runs its **full body** instead of early-returning, and passes (FullscreenApp not DesktopApp, no `playnite://` URI, working dir = the exe's parent) - planted install removed afterwards ## Note Until this ships, the plugin-side escape hatch is `"launcher": false` in `<config_dir>/plugin-state/playnite/config.json`. That was config-file-only and invisible in the UI; unom/punktfunk-plugin-playnite#5 surfaces it in Settings.
enricobuehler added 1 commit 2026-08-14 14:03:43 +00:00
fix(library): a per-user Playnite install is invisible to a SYSTEM host, and one tile killed the library
ci / web (pull_request) Successful in 1m13s
ci / bun-nix (pull_request) Successful in 1m25s
ci / rust (pull_request) Successful in 4m18s
ci / docs-site (pull_request) Successful in 4m27s
ci / rust-arm64 (pull_request) Successful in 4m39s
android / android (pull_request) Successful in 7m42s
bf741f8693
Syncing the Playnite plugin failed outright:

  PUT /library/provider/playnite failed: entries[9]: launch.value for kind
  launcher_ui names a launcher this host cannot open (playnite)

Two defects, and the second is why it cost every game rather than one tile.

1. The host looked for Playnite in the wrong registry hive and the wrong
   profile. `playnite_fullscreen_exe()` read HKEY_CURRENT_USER, then fell back
   to %LOCALAPPDATA% — but the Windows host is a LocalSystem service, so its
   HKCU is the SYSTEM hive (S-1-5-18) and its %LOCALAPPDATA% is
   C:\Windows\System32\config\systemprofile\AppData\Local. Playnite installs
   per-user by default, so both lookups miss on a default install. The doc
   comment reasoned correctly that Playnite is per-user and then read the one
   HKCU that cannot see it.

   It also hardcoded `…\Uninstall\Playnite`. Playnite ships an Inno Setup
   installer, and Inno registers `<AppId>_is1` — measured on a Windows box
   where Git and Inno itself appear as `Git_is1` and `Inno Setup 6_is1` — so
   that key matched nothing anywhere.

   Now: every loaded hive under HKEY_USERS plus both HKLM views, matched on
   DisplayName rather than key name, then `C:\Users\*\AppData\Local\Playnite`
   for the conventional install (and for a user whose hive is not loaded).

2. One unopenable tile 400'd the whole reconcile. The Playnite plugin appends
   a single launcher tile beside its games, so refusing the payload cost the
   operator the entire library — the same shape as the unservable-cover bug
   that sanitize_art_paths was introduced to fix, on the launch side this time.

   `valid_launcher_ui` conflated two different failures. Split into
   `known_launcher_ui` (vocabulary — a plugin bug, still a hard 400, because
   the author has no other way to find out) and `resolvable_launcher_ui`
   (environment — the launcher just is not installed here, which is a fact
   about the box). `sanitize_launcher_entries` drops only the latter, with one
   warn, and the games sync.
enricobuehler merged commit d0a3eca7b8 into main 2026-08-14 16:07:40 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#225