tui-cs/Terminal.Gui · error · InvalidOperationException
Themes must include an item named {DEFAULT_THEME_NAME}
Error message
Themes must include an item named {DEFAULT_THEME_NAME} What it means
Thrown by ThemeManager.SetThemes when the dictionary being assigned to the Themes property is non-null but does not contain the key "Default" (ThemeManager.DEFAULT_THEME_NAME). Terminal.Gui requires a Default theme to always exist because Theme getter, LoadHardCodedDefaults, and UpdateToCurrentValues all assume Themes[Theme] resolves and Theme defaults to "Default".
Source
Thrown at Terminal.Gui/Configuration/ThemeManager.cs:135
[JsonConverter (typeof (ConcurrentDictionaryJsonConverter<ThemeScope>))]
[ConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
public static ConcurrentDictionary<string, ThemeScope>? Themes
{
// Note: This property getter must be public; DeepClone depends on it.
get => GetThemes ();
internal set => SetThemes (value);
}
/// <summary>
/// INTERNAL: Setter for <see cref="Themes"/>.
/// </summary>
/// <param name="dictionary"></param>
/// <exception cref="InvalidOperationException"></exception>
private static void SetThemes (ConcurrentDictionary<string, ThemeScope>? dictionary)
{
if (dictionary is { } && !dictionary.ContainsKey (DEFAULT_THEME_NAME))
{
throw new InvalidOperationException ($"Themes must include an item named {DEFAULT_THEME_NAME}");
}
if (ConfigurationManager.Settings is { } && ConfigurationManager.Settings.TryGetValue ("Themes", out ConfigProperty? themes))
{
ConfigurationManager.Settings ["Themes"].PropertyValue = dictionary;
return;
}
throw new InvalidOperationException ("Settings is null.");
}
/// <summary>
/// INTERNAL: Returns the hard-coded Themes dictionary.
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private static ConcurrentDictionary<string, ThemeScope>? GetHardCodedThemes ()View on GitHub (pinned to 2e47b11478)
Solutions
- Ensure the dictionary contains a "Default" key (case-insensitive, StringComparer.InvariantCultureIgnoreCase is used internally) before assigning.
- Build the dictionary from GetHardCodedThemes() and then add/override entries rather than starting from an empty one.
- If loading from JSON, keep the "Default" theme block intact in the config file.
Example fix
// before
ThemeManager.Themes = new ConcurrentDictionary<string, ThemeScope> (
new Dictionary<string, ThemeScope> { ["Dark"] = darkScope });
// after
var themes = new ConcurrentDictionary<string, ThemeScope> (
new Dictionary<string, ThemeScope> { ["Default"] = ThemeManager.GetHardCodedThemes()!["Default"] },
StringComparer.InvariantCultureIgnoreCase);
themes ["Dark"] = darkScope;
ThemeManager.Themes = themes; Defensive patterns
Strategy: validation
Validate before calling
// Ensure 'Default' is present before assigning
if (!dictionary.ContainsKey (ThemeManager.DEFAULT_THEME_NAME))
dictionary [ThemeManager.DEFAULT_THEME_NAME] = ThemeManager.GetHardCodedThemes ()! [ThemeManager.DEFAULT_THEME_NAME];
ThemeManager.Themes = dictionary; Type guard
static bool HasDefaultTheme (ConcurrentDictionary<string, ThemeScope> d)
=> d.ContainsKey (ThemeManager.DEFAULT_THEME_NAME); Try / catch
try { ThemeManager.Themes = newThemes; }
catch (InvalidOperationException ex) when (ex.Message.Contains ("Default"))
{ /* add the Default entry and retry */ } Prevention
- Always seed a new themes dictionary from GetHardCodedThemes() so Default is preserved.
- Never rename or remove the "Default" key in config JSON.
- Add a unit test asserting Themes always contains Default after any assignment.
When it happens
Trigger: Programmatically assigning ThemeManager.Themes = new ConcurrentDictionary<string,ThemeScope>(...) that lacks a "Default" entry; deserializing a themes JSON where the "Default" key was renamed or removed; setting Themes before ConfigurationManager is initialized with an incomplete dictionary.
Common situations: A user edits config.json to rename the Default theme; a fork ships a themes dictionary without preserving Default; a test sets Themes to a fixture missing the key.
Related errors
- Theme cannot be set before ConfigurationManager is initializ
- Unexpected StartObject token when parsing Attribute: {reader
- {propertyName}: Both Foreground and Background colors must b
- DefaultKeyBindings dictionary is null. Initialize it before
- Driver '{driverName}' is not registered in DriverRegistry.
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/6e35df4a4bf54b4c.
Report an issue: GitHub.