Steam sync: misleading error and total loss of library when GetOwnedGames returns an empty response (Game details privacy, not profile privacy)
Bug description
The Steam source fails with Failed to load games. Check that your profile is set to public during the sync. even though the Steam profile is public. I verified this against Steam's own API: the message points at the wrong setting. The setting that actually matters is a separate one, Privacy Settings → Game details, not profile visibility.
While tracing it I found three further problems in the same code path: a failed load() wipes the previously synced library and completely blocks the import of locally installed games, even though Lutris already has everything it needs on disk.
1. The error message names the wrong setting
The profile is public. ISteamUser/GetPlayerSummaries returns:
{"steamid": "76561199805449168", "communityvisibilitystate": 3, "profilestate": 1, "personaname": "..."}and https://steamcommunity.com/profiles/<steamid>/?xml=1 confirms <privacyState>public</privacyState>, <visibilityState>3</visibilityState>, <isLimitedAccount>0</isLimitedAccount>.
Despite that, GetOwnedGames returns an empty response:
$ curl -s "https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=<KEY>&steamid=76561199805449168&format=json&include_appinfo=1&include_played_free_games=1"
{"response":{}} # HTTP 200The actual indicator is the games tab on the community profile, which redirects to the login page — i.e. the game list is not public:
$ curl -s "https://steamcommunity.com/profiles/<steamid>/games?tab=all&xml=1"
Found. Redirecting to /login/?redir=...GetOwnedGames does distinguish the two cases: with public game details and an empty library it returns {"response":{"game_count":0}}, while private game details give {"response":{}}. get_steam_library() (lutris/util/steam/config.py:145-149) treats both identically and returns [].
This explains the recurring "but my profile IS public" reports: #6764, #6830, #6648 (in #6648 @Leonix357 correctly points at game details in a comment, but the in-app message still sends people to the wrong place).
Suggestion: distinguish the two cases and name the exact setting, e.g. "Steam is not sharing your game list. In Steam, open Settings → Privacy and set Game details to Public (profile visibility alone is not enough)." It would also help to tell a genuinely empty library (game_count == 0) apart from one blocked by privacy.
2. wipe_game_cache() runs before load(), so a failed sync destroys the library
lutris/services/base.py:145-148:
self.wipe_game_cache()
self.load() # <- raises
self.load_icons()
self.add_installed_games()The cache is cleared unconditionally before any data is fetched. Any load() failure — privacy, a transient network drop, a timeout, a 5xx on Valve's side — leaves the user with an empty source even though things worked before they pressed "Reload". After my failed sync, the tables in pga.db are empty:
$ sqlite3 ~/.local/share/lutris/pga.db "select service, count(*) from service_games group by service;"
(empty)
$ sqlite3 ~/.local/share/lutris/pga.db "select id,name,runner,service from games;"
(empty)Suggestion: build the new list first and swap it in only after load() succeeds (or wrap the cache wipe in try/except with a rollback).
3. Locally installed games are ignored, even though Lutris detects them correctly
add_installed_games() never runs, because the exception from load() aborts do_reload(). And even if it did run, install_from_steam() (lutris/services/steam.py:122-125) returns immediately:
service_game = ServiceGameCollection.get_game(self.id, appid)
if not service_game:
return— the cache was just wiped in point 2. As a result, importing games that are physically present on disk is 100% dependent on the remote API, which it does not need to be.
All the local data is in place and Lutris sees it:
>>> from lutris.util.steam.config import get_active_steamid64, get_steamapps_dirs
>>> from lutris.util.steam.appmanifest import get_appmanifests
>>> get_active_steamid64()
'76561199805449168' # matches userdata/1845183440
>>> get_steamapps_dirs()
['/home/user/.local/share/Steam/steamapps', '/home/user/.local/share/Steam/compatibilitytools.d']
>>> len(list(get_appmanifests('/home/user/.local/share/Steam/steamapps')))
8There are 8 valid appmanifest_*.acf files on disk, including 3 real games (Anno 1602 – History Edition, Dragon Throne: Battle of Red Cliffs, Timber and Stone) plus Proton and the runtimes. None of them reach Lutris.
Suggestion: when load() fails, still run the local appmanifest scan and surface the error as a warning rather than a total failure. Locally installed games should also work offline and with a private profile.
4. Minor: the mostrecent warning, and the API key leaking into logs
get_steam_users() (lutris/util/steam/config.py:103) looks for a mostrecent key that the current Steam client does not write to my loginusers.vdf at all:
"users"
{
"76561199805449168"
{
"AccountName" "..."
"PersonaName" "..."
"RememberPassword" "1"
"WantsOfflineMode" "0"
"SkipOfflineModeWarning" "0"
"AutoLogin" "1"
"Timestamp" "1789226322"
}
}With a single account this is only log noise, but with several accounts get_active_steamid64() will then pick whichever account comes first in dict order instead of the most recently used one — while Timestamp, which is present in the file, would allow picking the right one. The message itself is also misleading: it lists steamid64 among the keys found in the file, even though Lutris inserts that key itself on line 102, right before the check.
Separately: logger.info("No games in response of %s", steam_games_url) (config.py:148) writes the full URL, including the Steam Web API key and the SteamID64, into the log. Users paste these logs into bug reports — Lutris' shared key is already visible that way in several public issues in this repository. Logging the URL without its query parameters would be enough.
How to Reproduce
Steps to reproduce the behavior:
- In Steam privacy settings, set My profile to "Public" and Game details to "Friends Only" or "Private".
- Install at least one game through the Steam client (I have 8
appmanifest_*.acffiles in~/.local/share/Steam/steamapps). - Start Lutris, select the Steam source and click "Reload".
- Result:
RuntimeError: Failed to load games. Check that your profile is set to public during the sync., the Steam source is empty, theservice_gamesandgamestables inpga.dbhave been wiped, and locally installed games are absent.
Expected behavior
- The error message names the Game details setting rather than general profile visibility, and distinguishes an empty library from one blocked by privacy.
- A failed sync does not wipe the previously synced library.
- Locally installed games are imported regardless of the Steam Web API response — the
appmanifestdata is entirely sufficient for that. mostrecentis not the only way to pick an account (fall back toTimestamp), and the API key does not end up in the log.
Log output
[INFO:2026-09-13 16:03:40,434:application]: Starting Lutris 0.5.22
[INFO:2026-09-13 16:03:40,547:startup]: "card0" is Intel Graphics (8086:64a0 103c:8d84 xe) Driver 26.1.8
[WARNING:2026-09-13 16:03:43,000:config]: Config key mostrecent not found in AccountName, PersonaName, RememberPassword, WantsOfflineMode, SkipOfflineModeWarning, AutoLogin, Timestamp, steamid64
[INFO:2026-09-13 16:03:43,629:config]: No games in response of https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=<REDACTED - see point 4>&steamid=76561199805449168&format=json&include_appinfo=1&include_played_free_games=1
[ERROR:2026-09-13 16:03:43,630:jobs]: Error while completing task <function BaseService.start_reload.<locals>.do_reload at 0x7fd6f84a7e20>: <class 'RuntimeError'> Failed to load games. Check that your profile is set to public during the sync.
Traceback (most recent call last):
File "/usr/lib/python3.14/site-packages/lutris/util/jobs.py", line 62, in target
result = self.function(*a, **kw)
File "/usr/lib/python3.14/site-packages/lutris/services/base.py", line 148, in do_reload
self.load()
~~~~~~~~~^^
File "/usr/lib/python3.14/site-packages/lutris/services/steam.py", line 98, in load
raise RuntimeError(_("Failed to load games. Check that your profile is set to public during the sync."))
RuntimeError: Failed to load games. Check that your profile is set to public during the sync.System Information
Lutris: 0.5.22 (distro package lutris-0.5.22-1.fc44.x86_64, not Flatpak)
OS: Fedora Linux 44 (KDE Plasma Desktop Edition)
Kernel: 7.2.4-200.fc44.x86_64
Session: KDE / Wayland
Python: 3.14.7
GPU: Intel Graphics (8086:64a0 103c:8d84) xe driver, Mesa 26.1.8
Steam: steam-1.0.0.87-1.fc44 (native), directory ~/.local/share/SteamSource: lutris/lutris