tui-cs/Terminal.Gui · error · JsonException
{propertyName}: "{mod}" is not a valid modifier.
Error message
{propertyName}: "{mod}" is not a valid modifier. What it means
Thrown by KeyCodeJsonConverter.Read (KeyCodeJsonConverter.cs:94-97) when an element of the "Modifiers" array string does not match any key in the modifier dictionary (which contains only 'Shift', 'Ctrl', 'Alt', matched case-insensitively). The lookup throws KeyNotFoundException, which is wrapped as a JsonException naming the invalid modifier.
Source
Thrown at Terminal.Gui/Configuration/KeyCodeJsonConverter.cs:96
case "modifiers":
if (reader.TokenType == JsonTokenType.StartArray)
{
while (reader.Read ())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
break;
}
string mod = reader.GetString ();
try
{
modifiers.Add (modifierDict [mod]);
}
catch (KeyNotFoundException e)
{
throw new JsonException ($"{propertyName}: \"{mod}\" is not a valid modifier.", e);
}
}
}
else
{
throw new JsonException (
$"{propertyName}: Expected an array of modifiers, but got \"{reader.TokenType}\"."
);
}
break;
default:
throw new JsonException ($"{propertyName}: Unexpected Key property.");
}
}
}
View on GitHub (pinned to 2e47b11478)
Solutions
- Use only the three supported modifier names: "Shift", "Ctrl", "Alt" (case-insensitive).
- Replace "Control" with "Ctrl".
- Remove unsupported platform modifiers; Terminal.Gui keybindings only model Shift/Ctrl/Alt.
- If you need a multi-modifier binding, list each in the array: ["Ctrl", "Shift"].
Example fix
// before
{ "Key": "A", "Modifiers": ["Control"] }
// after
{ "Key": "A", "Modifiers": ["Ctrl"] } Defensive patterns
Strategy: validation
Validate before calling
using System.Collections.Generic;
static readonly HashSet<string> ValidModifiers = new (System.StringComparer.OrdinalIgnoreCase)
{ "Shift", "Ctrl", "Alt" };
static bool AreModifiersValid (IEnumerable<string?>? mods)
=> mods?.All (m => m is not null && ValidModifiers.Contains (m)) ?? true;
if (!AreModifiersValid (modifierList))
{
// replace synonyms/typos before writing config
} Type guard
static bool IsValidModifier (string? mod)
=> mod is not null
&& (mod.Equals ("Shift", StringComparison.OrdinalIgnoreCase)
|| mod.Equals ("Ctrl", StringComparison.OrdinalIgnoreCase)
|| mod.Equals ("Alt", StringComparison.OrdinalIgnoreCase)); Try / catch
try
{
ConfigurationManager.Load (configJson);
}
catch (JsonException ex) when (ex.Message.Contains ("is not a valid modifier"))
{
// A Modifiers array entry is not Shift/Ctrl/Alt. Replace synonyms (Control->Ctrl) and reload.
} Prevention
- Use only "Shift", "Ctrl", "Alt" (case-insensitive) in Modifiers arrays.
- Replace "Control" with "Ctrl".
- Do not include platform-specific modifiers (Win/Command) — Terminal.Gui does not model them.
- List multiple modifiers as separate array elements.
When it happens
Trigger: A keybinding JSON object's "Modifiers" array contains a string other than Shift/Ctrl/Alt, e.g. ["Win"], ["Command"], ["Control"] (note: the dict key is "Ctrl", not "Control"), or a typo like ["Ctrk"].
Common situations: User writes "Control" instead of "Ctrl". User adds a platform modifier (Windows/Super/Command) that Terminal.Gui does not model. Typos. Inconsistent casing is tolerated, but alternate synonyms are not.
Related errors
- {propertyName}: Expected an array of modifiers, but got "{re
- Expected start of array for Key[].
- Expected string token in Key array, got {reader.TokenType}.
- {propertyName}: "{reader.GetString ()}" is not a valid Key.
- {propertyName}: Error parsing Key value: {ioe.Message}
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/42897f3de6123934.
Report an issue: GitHub.