Skip to content

[BUG] Installer fails when GitHub API is rate-limited — PluginLoader never downloaded (chown: cannot access '/home/deck/homebrew/services/*') #940

Description

@edufalcao

Please confirm

  • I have searched existing issues
  • This issue is not a duplicate of an existing one
  • I have checked the common issues section in the readme file
  • I have attached logs to this bug report (failure to include logs will mean your issue may not be responded to).

Bug Report Description

Installing Decky Loader 3.2.6 on SteamOS 3.8.14 (Legion Go) fails with the same symptoms as #492, whose recent comments show the problem resurfaced for others too:

jq: error (at <stdin>:1): Cannot index string with string "prerelease"
chmod: cannot access '/home/deck/homebrew/services/PluginLoader': No such file or directory
chown: cannot access '/home/deck/homebrew/services/*': No such file or directory

The installer still reports "Install finished", but the Decky menu never appears in Game Mode.

After investigating, I found the root cause. It is not SteamOS 3.8.14-specific — the installer fails unsafely when GitHub's unauthenticated API rate limit (60 req/hour per IP) is hit. The correlation with 3.8.14 is likely just reinstall waves after OS updates, plus ISPs using CGNAT where many customers share one IP and therefore one API quota.

Update: I submitted a fix for this: SteamDeckHomebrew/decky-installer#28 (also closes SteamDeckHomebrew/decky-installer#16, the same bug reported in the installer repo in 2024).

Root cause

The GUI installer (gui/user_install_script.sh in decky-installer, which is what the .desktop runs) fetches releases with an unauthenticated API call and has no rate-limit check (the CLI script has one; the GUI script never got it):

RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")

When the IP is rate-limited, the API returns HTTP 403 with an error object instead of a release array. Reproduced from my network:

HTTP/2 403
x-ratelimit-limit: 60
x-ratelimit-remaining: 0
x-ratelimit-used: 60
{"message":"API rate limit exceeded for <redacted IP>. ...","documentation_url":"..."}

Failure chain (explains every line of the log)

  1. jq's .[] iterates the error object's string values → the exact error from the logs:
    $ echo '{"message":"API rate limit exceeded for x.x.x.x.","documentation_url":"..."}' | jq -r 'first(.[] | select(.prerelease == false))'
    jq: error (at <stdin>:1): Cannot index string with string "prerelease"
    
  2. RELEASE/DOWNLOADURL end up empty → curl -L $DOWNLOADURL -o .../PluginLoader has no URL → PluginLoader is never downloaded → the chmod error.
  3. The "421 bytes" download seen in installer logs is the systemd service file from raw.githubusercontent.com (~421 bytes; not rate-limited since it's not the API) — that's why the installer still reaches "Install finished".
  4. At the end the script deletes the .service files from services/, leaving only dotfiles. Since * doesn't match dotfiles, the final chown -R root:root "${HOMEBREW_FOLDER}"/services/* glob matches nothing → chown: cannot access '/home/deck/homebrew/services/*': No such file or directory.

Counterfactual check: the identical query, same network, same minute, but authenticated, succeeds and returns v3.2.6 — so the endpoint, payload, and jq filter are all fine; the only differing variable is the unauthenticated per-IP quota.

Second defect that makes this worse

The script runs rm -rf "${HOMEBREW_FOLDER}/services" before contacting the API. So a rate-limited "update" also destroys a previously working install — which matches the "Decky disappeared, and reinstalling also fails" reports in #492.

Suggested fixes (in decky-installer)

Implemented in SteamDeckHomebrew/decky-installer#28:

  1. For the release channel, skip the API entirely: https://github.com/SteamDeckHomebrew/decky-loader/releases/latest/download/PluginLoader works unauthenticated with no rate limit (verified: HTTP 200, ~27 MB).
  2. Validate the version/download URL and abort with a visible error before rm -rf services (for the prerelease channel, which still needs the API).
  3. Download to a temp file and only replace the existing install after a verified download.

Workaround for affected users

Either retry on a different network (e.g. phone hotspot — fresh IP quota), wait for the hourly quota reset, or install manually (Desktop Mode → Konsole) — these URLs don't touch the rate-limited API:

mkdir -p ~/homebrew/services ~/homebrew/plugins
curl -L https://github.com/SteamDeckHomebrew/decky-loader/releases/latest/download/PluginLoader -o ~/homebrew/services/PluginLoader
chmod +x ~/homebrew/services/PluginLoader
echo "v3.2.6" > ~/homebrew/services/.loader.version
touch ~/.steam/steam/.cef-enable-remote-debugging
curl -L https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-release.service -o /tmp/plugin_loader.service
sed -i "s|\${HOMEBREW_FOLDER}|$HOME/homebrew|g" /tmp/plugin_loader.service
sudo cp /tmp/plugin_loader.service /etc/systemd/system/plugin_loader.service
sudo systemctl daemon-reload
sudo systemctl enable --now plugin_loader

To confirm you're hitting this before reinstalling, run:

curl -s https://api.github.com/rate_limit | jq .resources.core

If remaining is 0, the installer cannot work until the reset time shown.

Expected Behaviour

The installer should detect the rate-limited API response and abort with a clear error (before deleting the existing services/ folder), instead of reporting "Install finished" with no PluginLoader binary installed.

SteamOS version

3.8.14 Stable

Selected Update Channel

Stable

Decky Loader Version

3.2.6

Plugin Info

N/A

Have you modified the read-only filesystem at any point?

No

Backend Logs

N/A — the PluginLoader binary is never downloaded, so there is no backend to produce logs. The relevant installer output is in the description above.

Frontend Logs

N/A — Decky never loads, so no frontend logs exist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions