Do not abort startup when the config file cannot be written (#1340)

* Don't abort startup when the config file can't be written

ConfigFile.Save opens the file for writing with no exception handling and is invoked automatically via SaveOnConfigSet while settings are bound during startup, before logging is initialized. A read-only or otherwise unwritable config file (a read-only install directory, a file locked by another process or antivirus) therefore throws UnauthorizedAccessException out of the early startup path and shows the Failed to start BepInEx dialog.
This commit is contained in:
HetCreep authored and GitHub committed 2026-06-11 14:49:59 +02:00
1 parent bd467c9af6
commit da64b220eb
1 file changed
+20 -2
+20 -2
View File
@@ -34,7 +34,7 @@ public class ConfigFile : IDictionary<ConfigDefinition, ConfigEntryBase>
if (File.Exists(ConfigFilePath)) if (File.Exists(ConfigFilePath))
Reload(); Reload();
else if (saveOnInit) Save(); else if (saveOnInit) TrySave();
} }
public static ConfigFile CoreConfig { get; } = new(Paths.BepInExConfigPath, true); public static ConfigFile CoreConfig { get; } = new(Paths.BepInExConfigPath, true);
@@ -352,6 +352,24 @@ public class ConfigFile : IDictionary<ConfigDefinition, ConfigEntryBase>
} }
} }
/// <summary>
/// Writes the config to disk like <see cref="Save" />, but logs and swallows I/O failures (e.g. a read-only
/// or locked config file) instead of throwing. Used only for the creation-time saves (initial file write and
/// new-entry binds), which can run before logging is up; explicit saves and setting changes still throw.
/// </summary>
private void TrySave()
{
try
{
Save();
}
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
{
Logger.Log(LogLevel.Warning,
$"Could not write config file {ConfigFilePath}: {e.Message}. Continuing with the in-memory configuration.");
}
}
#endregion #endregion
#region Wraps #region Wraps
@@ -452,7 +470,7 @@ public class ConfigFile : IDictionary<ConfigDefinition, ConfigEntryBase>
} }
if (SaveOnConfigSet) if (SaveOnConfigSet)
Save(); TrySave();
return entry; return entry;
} }