tui-cs/Terminal.Gui · error · InvalidOperationException
Unsupported dictionary type: {type}. Only Dictionary<,> and
Error message
Unsupported dictionary type: {type}. Only Dictionary<,> and ConcurrentDictionary<,> are supported. What it means
Thrown by DeepCloner.CloneDictionary when the source is an IDictionary whose closed generic type is neither System.Collections.Generic.Dictionary<,> nor System.Collections.Concurrent.ConcurrentDictionary<,>. The cloner only knows how to reconstruct those two concrete dictionary shapes (DeepCloner.cs:297-312); any other two-argument generic dictionary (SortedDictionary, ReadOnlyDictionary, custom IDictionary<,>) is rejected with InvalidOperationException.
Source
Thrown at Terminal.Gui/Configuration/DeepCloner.cs:309
CheckForUnsupportedDictionaryTypes (type);
}
Type [] genericArgs = type.GetGenericArguments ();
Type dictType;
if (genericArgs.Length == 2)
{
if (type.GetGenericTypeDefinition () == typeof (Dictionary<,>))
{
dictType = typeof (Dictionary<,>).MakeGenericType (genericArgs);
}
else if (type.GetGenericTypeDefinition () == typeof (ConcurrentDictionary<,>))
{
dictType = typeof (ConcurrentDictionary<,>).MakeGenericType (genericArgs);
}
else
{
throw new InvalidOperationException (
$"Unsupported dictionary type: {type}. Only Dictionary<,> and ConcurrentDictionary<,> are supported.");
}
}
else
{
dictType = typeof (Dictionary<object, object>);
}
object? comparer = type.GetProperty ("Comparer")?.GetValue (source);
IDictionary tempDict = CreateDictionaryInstance (dictType, comparer);
visited.TryAdd (source, tempDict);
object? lastKey = null;
try
{
// Clone all key-value pairsView on GitHub (pinned to 2e47b11478)
Solutions
- Use Dictionary<TKey,TValue> or ConcurrentDictionary<TKey,TValue> for any dictionary that will be deep-cloned by the configuration system.
- If ordering is needed, sort keys when reading rather than using SortedDictionary<,>.
- If read-only semantics are needed, keep the storage as Dictionary<,> and expose a read-only view wrapper that is NOT the cloned property type.
- For custom IDictionary<,> subclasses, either inherit from Dictionary<,>/ConcurrentDictionary<,> directly (so GetGenericTypeDefinition still matches) or move the custom type out of the clonable property graph.
- If the dictionary is nested in a ConfigProperty, set ConfigProperty.Immutable or restructure so it is not deep-cloned.
Example fix
// before
public SortedDictionary<string, Scheme> Schemes { get; set; }
// after
public Dictionary<string, Scheme> Schemes { get; set; } = new (); Defensive patterns
Strategy: type-guard
Validate before calling
static bool IsSupportedDictionary (object? obj)
{
if (obj is not IDictionary) return false;
Type t = obj.GetType ();
if (!t.IsGenericType) return true; // non-generic path uses Dictionary<object,object>
Type gtd = t.GetGenericTypeDefinition ();
return gtd == typeof (Dictionary<,>) || gtd == typeof (ConcurrentDictionary<,>);
}
if (!IsSupportedDictionary (myDict))
{
myDict = new Dictionary<...> (myDict); // normalize
} Type guard
static bool IsSupportedDictionary<T> (T value) where T : notnull
{
Type t = value.GetType ();
if (!t.IsGenericType) return value is IDictionary;
Type gtd = t.GetGenericTypeDefinition ();
return gtd == typeof (Dictionary<,>) || gtd == typeof (ConcurrentDictionary<,>);
} Try / catch
try
{
var clone = DeepCloner.DeepClone (configWithDict);
}
catch (InvalidOperationException ex) when (ex.Message.Contains ("Unsupported dictionary type"))
{
// Convert the offending dictionary to Dictionary<,> and retry.
} Prevention
- Standardize on Dictionary<,> or ConcurrentDictionary<,> for all configuration dictionaries.
- Avoid SortedDictionary, ReadOnlyDictionary, and custom IDictionary subclasses in clonable graphs.
- If a custom dictionary subclass is needed, inherit from Dictionary<,>/ConcurrentDictionary<,> directly.
- Add a clone-roundtrip test for every config scope type.
When it happens
Trigger: A configuration property holds a SortedDictionary<TKey,TValue>, ReadOnlyDictionary<TKey,TValue>, OrderedDictionary, a custom class implementing IDictionary<,>, or any third-party dictionary type. DeepClone is invoked on the owning scope/config object, reaches CloneDictionary, finds genericArgs.Length==2, but type.GetGenericTypeDefinition() matches neither supported type.
Common situations: User switches a config property from Dictionary to SortedDictionary for deterministic key ordering. User wraps a dictionary in ReadOnlyDictionary<,> for immutability. A third-party theme/config extension introduces a custom IDictionary<,> subclass stored in a ThemeScope.
Related errors
- Cannot create instance of type {type.FullName}. No parameter
- Cloning of collection type {type.Name} is not supported with
- Error cloning dictionary ({source}) (last key was "{lastKey}
- Cloning of frozen or immutable dictionaries like {type.Name}
- Error Applying Configuration Change: {tie.InnerException.Mes
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/655cc0d4ec79335a.
Report an issue: GitHub.