From ab0742742d63c3c9c75ab6cf1413613eaf8c4cba Mon Sep 17 00:00:00 2001 From: mattchristenson Date: Tue, 29 Sep 2026 22:46:55 -0500 Subject: [PATCH] fix: start with the default window rules when windows.json is empty or broken Reading ~/.config/forge/config/windows.json did JSON.parse() without a fallback, so an empty file or a typo in it made enabling Forge fail ("an error occurred while loading this extension", #415). Forge itself could leave the file empty: loadFile() wrote the default rules to a new file without closing the stream. When the file is empty, isn't valid JSON or has no overrides list, use the default rules and say so with console.warn (Forge's Logger is silent in release builds). The next change of the rules replaces the file, so first keep a copy of the broken one as windows.json.bak. Close the stream after writing. Fixes #415 Co-Authored-By: Claude Opus 5.5 --- lib/shared/settings.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/shared/settings.js b/lib/shared/settings.js index f0932107..8ece9c51 100644 --- a/lib/shared/settings.js +++ b/lib/shared/settings.js @@ -110,6 +110,8 @@ export class ConfigManager extends GObject.Object { const defaultContents = this.loadFileContents(defaultFile); Logger.trace(defaultContents); createdStream.write_all(defaultContents, null); + // close it, or the file can stay empty (#415) + createdStream.close(null); } } } @@ -137,7 +139,29 @@ export class ConfigManager extends GObject.Object { if (success) { const windowConfigContents = imports.byteArray.toString(contents); Logger.trace(`${windowConfigContents}`); - windowProps = JSON.parse(windowConfigContents); + try { + windowProps = JSON.parse(windowConfigContents); + if (!Array.isArray(windowProps?.overrides)) throw new Error("no overrides list"); + } catch (e) { + // An empty or broken file mustn't stop Forge from starting (#415): use the default + // rules. Forge rewrites the file on the next change of the rules (e.g. a window closing), + // so keep a copy of it first. + let backup = ""; + if (windowConfigContents.trim()) { + try { + const backupFile = Gio.File.new_for_path(`${windowConfigFile.get_path()}.bak`); + windowConfigFile.copy(backupFile, Gio.FileCopyFlags.OVERWRITE, null, null); + backup = `; it was copied to ${backupFile.get_path()}`; + } catch (copyError) { + backup = `; it couldn't be copied (${copyError.message})`; + } + } + console.warn( + `Forge: ${windowConfigFile.get_path()} is not valid (${e.message}); ` + + `using the default window rules${backup}` + ); + windowProps = this.loadDefaultWindowConfigContents(); + } } return windowProps; }