#6891·lutris

Game configuration dialog saves a launch-impossible config: no required-field validation (only Steam's appid is checked) and is_installed = True set unconditionally

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

Bug description

The game configuration dialog lets you save a completely empty configuration ({}) for the wine runner and still marks the game as installed. The game lands in the library in a state where it cannot be launched, and the error at startup blames an installer that was never run.

In my case this happened to a Steam game that had been imported correctly moments earlier — changing the runner in the configuration dialog wiped the working Steam configuration and left an empty file behind.

1. is_valid() does not check required fields other than Steam's appid

lutris/gui/config/game_common.py:352-387:

python
if not self.runner_name:
    ErrorDialog(_("Runner not provided"), parent=self); return False
if not game_info_box.name_entry.get_text():
    ErrorDialog(_("Please fill in the name"), parent=self); return False
if self.runner_name == "steam" and not self.lutris_config.game_config.get("appid"):
    ErrorDialog(_("Steam AppID not provided"), parent=self); return False
...
for config in ["game", "runner"]:
    for k, v in getattr(self.lutris_config, config + "_config").items():
        option = runner_instance.find_option(config + "_options", k)
        ...

The steam runner has an appid check, but wine (and every other runner) has no equivalent for exe. The validation loop iterates only over keys already present in the configuration, so for an empty configuration it never runs once and passes trivially. The result on disk:

$ cat ~/.local/share/lutris/games/dragon-throne-battle-of-red-cliffs-1789310162.yml
{}

2. on_save() sets is_installed = True unconditionally

python
self.game.is_installed = True
self.game.config = self.lutris_config

A game with no executable and no directory gets marked as installed. In the database:

bash
$ sqlite3 ~/.local/share/lutris/pga.db \
    "select name,runner,service,service_id,installed,installer_slug,configpath from games where id=3;"
Dragon Throne: Battle of Red Cliffs|wine|steam|2633500|1|steam-2633500|dragon-throne-battle-of-red-cliffs-1789310162

The row is internally inconsistent: service=steam and installer_slug=steam-2633500 (left over from the correct Steam import), but runner=wine and an empty configuration. Lutris neither detects this inconsistency nor offers a way to repair it.

3. The error message names the wrong cause

lutris.exceptions.MissingGameExecutableError: This game has no executable set. The install process didn't finish properly.

The second sentence is untrue — no install process was ever run. The game came from a Steam import and is entirely present on disk (412 MB, StateFlags 4, LastPlayed set). The message sends the user toward reinstalling instead of toward the empty Executable field in the configuration.

4. Changing the runner orphans the working service configuration

Timeline from the log:

16:35:18  add_installed_games() creates a correct entry: games/steam-2633500-1789310118.yml
16:35:52  [INFO:game_common]: Creating new configuration with runner wine
16:36:02  [ERROR:wine]: The game doesn't have an executable

The correct Steam configuration is still on disk, but nothing points to it any more:

$ cat ~/.local/share/lutris/games/steam-2633500-1789310118.yml
system: {}
null: {}
game:
  appid: '2633500'

The dialog warns that changing the runner "will reset the full configuration for this game and is not reversible", but for a service-linked game this also means severing the link to an existing Steam installation, with no way back from the UI. The orphaned file stays on disk.

Incidentally: the null: {} key in saved configurations looks like a separate serialization bug — a None key ends up in the YAML.

How to Reproduce

Steps to reproduce the behavior:

  1. Sync the Steam source so that a locally installed game is imported (mine: Dragon Throne: Battle of Red Cliffs, appid 2633500, runner steam).
  2. Choose Configure on that game.
  3. In the Runner field, change Steam to Wine and confirm the warning.
  4. Save without filling in the Executable field.
  5. The save goes through without any warning, the game stays marked as installed, and games/<slug>-<id>.yml contains {}.
  6. Launch the game → MissingGameExecutableError.

Expected behavior

  1. is_valid() blocks the save when a field required by the selected runner is missing (for wine: exe), just as it does for Steam's appid. Preferably generically, based on the runner's option metadata, rather than a single check hardcoded for steam.
  2. is_installed is not set to True for a configuration that cannot start.
  3. MissingGameExecutableError does not blame the installer when no installation ever took place — it should point at the empty field in the configuration.
  4. Changing the runner of a service-linked game either clears the service link or allows it to be restored from the UI; orphaned configuration files do not stay on disk.

Log output

bash
[INFO:2026-09-13 16:35:52,819:game_common]: Creating new configuration with runner wine
[ERROR:2026-09-13 16:36:02,602:wine]: The game doesn't have an executable
[WARNING:2026-09-13 16:36:02,674:game]: No path found in LutrisConfig(level=game, game_config_id=dragon-throne-battle-of-red-cliffs-1789310162, runner=wine)
[WARNING:2026-09-13 16:36:02,674:path_cache]: No path for Dragon Throne: Battle of Red Cliffs (wine)
[ERROR:2026-09-13 16:36:04,794:wine]: The game doesn't have an executable
[ERROR:2026-09-13 16:36:04,798:exception_backstops]: Dragon Throne: Battle of Red Cliffs (wine) has encountered an error: This game has no executable set. The install process didn't finish properly.
Traceback (most recent call last):
  File "/usr/lib/python3.14/site-packages/lutris/exception_backstops.py", line 35, in wrapper
    result = function(*args, **kwargs)
  File "/usr/lib/python3.14/site-packages/lutris/game.py", line 687, in configure_game
    gameplay_info = self.get_gameplay_info(launch_ui_delegate)
  File "/usr/lib/python3.14/site-packages/lutris/game.py", line 626, in get_gameplay_info
    gameplay_info = self.runner.play()
  File "/usr/lib/python3.14/site-packages/lutris/runners/wine.py", line 1336, in play
    raise MissingGameExecutableError(filename=game_exe)
lutris.exceptions.MissingGameExecutableError: This game has no executable set. The install process didn't finish properly.
[WARNING:2026-09-13 16:36:04,840:game]: The game has run for a very short time, did it crash?

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