felixse/FluentTerminal · error · ParseThemeException
Color node was not a dictionary
Error message
Color node was not a dictionary
What it means
Thrown inside GetColorString when an individual color entry (e.g. "Ansi 0 Color", "Background Color") is not itself a DictionaryNode. In iTerm themes each color is a dictionary containing "Red Component", "Green Component", "Blue Component" keys. If the entry is a scalar RealNode, string, or data node, the `colorNode as DictionaryNode` cast is null and ParseThemeException is thrown.
Source
Thrown at FluentTerminal.App.Services/Implementation/ITermThemeParser.cs:110
Yellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi3Color]),
Blue = GetColorString(themeDictionary[ITermThemeKeys.Ansi4Color]),
Magenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi5Color]),
Cyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi6Color]),
White = GetColorString(themeDictionary[ITermThemeKeys.Ansi7Color]),
BrightBlack = GetColorString(themeDictionary[ITermThemeKeys.Ansi8Color]),
BrightRed = GetColorString(themeDictionary[ITermThemeKeys.Ansi9Color]),
BrightGreen = GetColorString(themeDictionary[ITermThemeKeys.Ansi10Color]),
BrightYellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi11Color]),
BrightBlue = GetColorString(themeDictionary[ITermThemeKeys.Ansi12Color]),
BrightMagenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi13Color]),
BrightCyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi14Color]),
BrightWhite = GetColorString(themeDictionary[ITermThemeKeys.Ansi15Color]),
};
}
private string GetColorString(PNode colorNode, byte alpha = Byte.MaxValue)
{
var dictionaryNode = colorNode as DictionaryNode ?? throw new ParseThemeException("Color node was not a dictionary");
var red = dictionaryNode[ITermThemeColorKeys.RedComponent] as RealNode ?? throw new ParseThemeException("Red node value was not a real number");
var green = dictionaryNode[ITermThemeColorKeys.GreenComponent] as RealNode ?? throw new ParseThemeException("Green node value was not a real number");
var blue = dictionaryNode[ITermThemeColorKeys.BlueComponent] as RealNode ?? throw new ParseThemeException("Blue node value was not a real number");
if (alpha == byte.MaxValue)
{
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
}
else
{
return $"rgba({GetByteValue(red):G}, {GetByteValue(green):G}, {GetByteValue(blue):G}, {ToDoubleString(alpha)})";
}
}
private byte GetByteValue(RealNode node)
{
var doubleValue = node.Value * Byte.MaxValue;View on GitHub (pinned to ba83ec485e)
Solutions
- Open the .itermcolors file and verify every "Ansi N Color" / "Background Color" / "Foreground Color" entry is a <dict> containing Red/Green/Blue Component keys.
- Re-export the theme from iTerm2 to regenerate the correct nested-dict structure.
- Catch ParseThemeException at the call site and report which theme failed so the user can replace it.
Example fix
// before
var dictionaryNode = colorNode as DictionaryNode ?? throw new ParseThemeException("Color node was not a dictionary");
// after - tolerate scalar colors and skip, logging the bad entry
if (!(colorNode is DictionaryNode dictionaryNode))
throw new ParseThemeException($"Color node was not a dictionary (got {colorNode?.GetType().Name})"); Defensive patterns
Strategy: type-guard
Validate before calling
// After loading the root dict, check each color entry is a dict before GetColorString.
foreach (var key in new[] { "Background Color", "Ansi 0 Color" /*...*/ })
{
if (node[key] is PListNet.Nodes.DictionaryNode)
continue;
return Task.FromException<TerminalTheme>(new ParseThemeException($"{key} is not a dictionary"));
} Type guard
static bool IsColorDictionary(PNode n) => n is PListNet.Nodes.DictionaryNode d
&& d.ContainsKey("Red Component") && d.ContainsKey("Green Component") && d.ContainsKey("Blue Component"); Try / catch
try { theme = await parser.Parse(name, stream); }
catch (ParseThemeException ex) when (ex.Message.Contains("Color node"))
{ notifyUser("Theme has a malformed color entry."); } Prevention
- Use themes exported directly by iTerm2 to guarantee nested color dicts.
- Add a structural validator that checks every expected color key is a dict before parsing.
- Catch ParseThemeException at import time and skip/flag the bad theme.
When it happens
Trigger: GetColors iterates every themeDictionary[ITermThemeKeys.*] color key and calls GetColorString. If any single color entry is a plain number/string instead of a dict (e.g. someone replaced a color sub-dict with a hex string "#ff0000"), the cast fails.
Common situations: Theme was converted by a tool that flattened colors to scalar hex strings; a manually edited plist where one color entry lost its nested <dict>; a partially-compatible iTerm theme variant.
Related errors
- Root node was not a dictionary.
- Red node value was not a real number
- Green node value was not a real number
- UserInfo part contains {parts.Length} elements.
- Invalid link.
AI-assisted analysis of felixse/FluentTerminal@ba83ec485e (2026-08-13).
Data as JSON: /api/errors/7817ef8cea248a1c.
Report an issue: GitHub.