d2phap/ImageGlass · critical · FileLoadException
IGE: Could not parse merged config.
Error message
IGE: Could not parse merged config.
What it means
Thrown by Config_Static when JsonSerializer.Deserialize returns null after merging the default, user, CLI-override, and admin JSON config layers into a single byte array. The deserializer returned null rather than throwing, meaning the merged JSON parsed to a JSON null (or the merged byte array was empty/null) instead of a valid Config object. Wrapped in a FileLoadException and stored on LoadingException.
Source
Thrown at source/ImageGlass.Lib/Settings/Config_Static.cs:589
// 4. read igconfig.admin.json (BaseDir, then ConfigDir). Merge-only layer.
using var adminDoc = ReadAdminConfigDocument();
// 5. drop incompatible older layers; flag an incompatible user file so startup can warn
var effectiveDefaultDoc = IsCompatibleConfigLayer(defaultDoc) ? defaultDoc : null;
var effectiveAdminDoc = IsCompatibleConfigLayer(adminDoc) ? adminDoc : null;
var effectiveUserDoc = userDoc;
if (!IsCompatibleConfigLayer(userDoc))
{
effectiveUserDoc = null;
IncompatibleUserConfigPath = userConfigPath;
}
// 6. merge the compatible layers into a single JSON byte array
var mergedJson = MergeJsonLayers(effectiveDefaultDoc, effectiveUserDoc, cliOverrides, effectiveAdminDoc);
// 7. deserialize the merged JSON into Config
var config = JsonSerializer.Deserialize(mergedJson, jsonContext.Config)
?? throw new FileLoadException("IGE: Could not parse merged config.");
// 8. migrate if config version changed
appConfig = MigrateUserConfigFile(config);
// 9. apply persisted tool configs derived from the merged config
ApplyPersistedToolConfigs(appConfig);
}
catch (Exception ex)
{
LoadingException = ex;
}
appConfig ??= new();
// seed pre-configured external tools; also runs on a failed load so they still work
EnsurePredefinedTools(appConfig);
return appConfig;View on GitHub (pinned to 4a3c4fecef)
Solutions
- Inspect LoadingException.InnerException and the mergedJson bytes (log them at startup) to see if the merged document is `null`, empty, or a non-object.
- Restore or regenerate igconfig.json — delete it so the app re-creates it from defaults on next start.
- Ensure the default config layer (shipped with the app) is present and is a JSON object, not null.
- If caused by CLI overrides (--Config:* args), check that the override syntax produces a value, not a whole-object replacement with null.
Example fix
// before
var config = JsonSerializer.Deserialize(mergedJson, jsonContext.Config)
?? throw new FileLoadException("IGE: Could not parse merged config.");
// after — include a snippet of the merged JSON to make the null parse diagnosable
var config = JsonSerializer.Deserialize(mergedJson, jsonContext.Config);
if (config is null)
{
var preview = mergedJson is null ? "<null>" : Encoding.UTF8.GetString(mergedJson);
if (preview.Length > 200) preview = preview[..200] + "...";
throw new FileLoadException($"IGE: Could not parse merged config (parsed to null). Merged JSON: {preview}");
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before deserialize, confirm the merged JSON is a non-null object document.
if (mergedJson is null || mergedJson.Length == 0) throw new FileLoadException("Merged config is empty");
using var doc = JsonDocument.Parse(mergedJson);
if (doc.RootElement.ValueKind != JsonValueKind.Object) throw new FileLoadException($"Merged config is {doc.RootElement.ValueKind}, expected object"); Type guard
static bool IsMergedObject(byte[] json)
{
if (json is null || json.Length == 0) return false;
using var d = JsonDocument.Parse(json);
return d.RootElement.ValueKind == JsonValueKind.Object;
} Try / catch
try { Config.Load(...); }
catch (FileLoadException ex) when (ex.Message.Contains("Could not parse merged config"))
{ _log.Error($"Config parse failed: {ex.Message}"); Config.RestoreDefaults(); /* or prompt user */ } Prevention
- Validate the merged JSON is an object (not null/empty/scalar) before deserializing.
- Always ship a known-good default config layer so the merge never produces a null document.
- Sanitize user-edited igconfig.json: reject literal `null` at the document root.
- On parse failure, back up the corrupt user config and regenerate from defaults.
When it happens
Trigger: Produced at Config_Static.cs:589 when JsonSerializer.Deserialize(mergedJson, jsonContext.Config) returns null. Reached at the end of config load after MergeJsonLayers — the merge produced a JSON document that deserializes to null.
Common situations: A hand-edited igconfig.json reduced to the literal `null`; a CLI override or admin layer that overwrote the whole object with null; MergeJsonLayers returning an empty byte array when all layers were null (e.g. first run with no default shipped); a corrupt default config shipped with the build; a JSON merge that produced `null` via deep override semantics.
Related errors
- IGE: Unable to load '{themeFolderName}' theme pack. Please m
- Unable to load '{themeFolderName}' theme pack. Please make s
- IGE: Metadata too large: {contentLength} bytes.
- Button ID required.
- A button with the ID '{0}' has already been defined. Please
AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13).
Data as JSON: /api/errors/bd43768bfa0db39d.
Report an issue: GitHub.