#6890·lutris

Steam sync: misleading error and total loss of library when GetOwnedGames returns an empty response (Game details privacy, not profile privacy)

Author: kacperpaczosCreated Sep 13, 2026Updated Sep 13, 2026

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:

json
{"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 200

The 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:

python
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:

python
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:

python
>>> 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')))
8

There 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:

  1. In Steam privacy settings, set My profile to "Public" and Game details to "Friends Only" or "Private".
  2. Install at least one game through the Steam client (I have 8 appmanifest_*.acf files in ~/.local/share/Steam/steamapps).
  3. Start Lutris, select the Steam source and click "Reload".
  4. Result: RuntimeError: Failed to load games. Check that your profile is set to public during the sync., the Steam source is empty, the service_games and games tables in pga.db have been wiped, and locally installed games are absent.

Expected behavior

  1. The error message names the Game details setting rather than general profile visibility, and distinguishes an empty library from one blocked by privacy.
  2. A failed sync does not wipe the previously synced library.
  3. Locally installed games are imported regardless of the Steam Web API response — the appmanifest data is entirely sufficient for that.
  4. mostrecent is not the only way to pick an account (fall back to Timestamp), and the API key does not end up in the log.

Log output

bash
[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

bash
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/Steam