tui-cs/Terminal.Gui · error · NotSupportedException
Cloning of frozen or immutable dictionaries like {type.Name}
Error message
Cloning of frozen or immutable dictionaries like {type.Name} is not supported. What it means
Thrown by DeepCloner.CheckForUnsupportedDictionaryTypes (DeepCloner.cs:446-465) when the type or any of its base types is a generic type whose full name starts with 'System.Collections.Frozen' or 'System.Collections.Immutable'. Frozen and immutable dictionaries have no mutation API, so the cloner (which builds a mutable copy) cannot clone them. This is an explicit NotSupportedException.
Source
Thrown at Terminal.Gui/Configuration/DeepCloner.cs:459
// Fallback to parameterless constructor if comparer constructor is not available
return (IDictionary)Activator.CreateInstance (dictType)!;
}
}
private static void CheckForUnsupportedDictionaryTypes (Type type)
{
Type? currentType = type;
while (currentType != null && currentType != typeof (object))
{
if (currentType.IsGenericType)
{
string? genericTypeName = currentType.GetGenericTypeDefinition ().FullName;
if (genericTypeName != null
&& (genericTypeName.StartsWith ("System.Collections.Frozen") || genericTypeName.StartsWith ("System.Collections.Immutable")))
{
throw new NotSupportedException ($"Cloning of frozen or immutable dictionaries like {type.Name} is not supported.");
}
}
currentType = currentType.BaseType;
}
}
#endregion Dictionary Support
#region AOT Support
private static TScopeT CloneScope<TScopeT> (TScopeT scope, ConcurrentDictionary<object, object> visited)
where TScopeT : Scope<TScopeT>, new()
{
TScopeT clonedScope = new ();
visited.TryAdd (scope, clonedScope);
foreach (KeyValuePair<string, ConfigProperty> kvp in scope)View on GitHub (pinned to 2e47b11478)
Solutions
- Store the dictionary as a plain Dictionary<,> or ConcurrentDictionary<,> in the clonable property; call ToFrozenDictionary/ToImmutableDictionary only at read sites that do not clone.
- If immutability is required across the boundary, convert to Dictionary just before assigning to the ConfigProperty.
- For ThemeScope/SettingsScope, these are already ConcurrentDictionary and are handled by the typed path at line 251 — do not replace them with frozen variants.
- If a nested property is the offender, mark it as a simple/non-cloned type or wrap it so DeepCloneInternal returns it by reference (e.g. implement a custom scope clone).
Example fix
// before
public FrozenDictionary<string, Scheme> Schemes { get; set; }
= themes.ToFrozenDictionary ();
// after — keep clonable storage mutable; freeze only at read time
public Dictionary<string, Scheme> Schemes { get; set; } = new (themes);
public FrozenDictionary<string, Scheme> SchemesFrozen => Schemes.ToFrozenDictionary (); Defensive patterns
Strategy: validation
Validate before calling
static bool IsClonableDictionary (object? obj)
{
if (obj is not IDictionary) return false;
Type? t = obj.GetType ();
while (t is not null && t != typeof (object))
{
if (t.IsGenericType)
{
string? name = t.GetGenericTypeDefinition ().FullName;
if (name is not null
&& (name.StartsWith ("System.Collections.Frozen")
|| name.StartsWith ("System.Collections.Immutable")))
{
return false;
}
}
t = t.BaseType;
}
return true;
}
if (!IsClonableDictionary (myDict))
{
myDict = new Dictionary<,>(myDict); // thaw before cloning
} Type guard
static bool IsMutableDictionary (object obj)
{
string? ns = obj.GetType ().Namespace;
return obj is IDictionary
&& ns != "System.Collections.Frozen"
&& ns != "System.Collections.Immutable";
} Try / catch
try
{
var clone = DeepCloner.DeepClone (configWithDict);
}
catch (NotSupportedException ex) when (ex.Message.Contains ("frozen or immutable"))
{
// Convert to a mutable Dictionary<,> before assigning to the ConfigProperty.
} Prevention
- Keep clonable dictionary storage mutable (Dictionary/ConcurrentDictionary); freeze only at non-cloning read sites.
- Never store FrozenDictionary/ImmutableDictionary in a ConfigProperty.
- Add a test that DeepClones every config scope to surface frozen/immutable usage.
- Audit third-party theme extensions for immutable collection usage.
When it happens
Trigger: A configuration property holds a FrozenDictionary<TKey,TValue>, ImmutableDictionary<TKey,TValue>, ImmutableSortedDictionary<,>, or any type derived from those families. CloneDictionary reaches the unsupported-type check before attempting construction and rejects it.
Common situations: A user calls ToFrozenDictionary()/ToImmutableDictionary() on a schemes/themes map for performance and stores the result in a clonable ConfigProperty. A .NET 8+ migration introduces FrozenDictionary in a shared config object. A test fixture builds an ImmutableDictionary and passes it through ConfigurationManager.Apply.
Related errors
- Unsupported dictionary type: {type}. Only Dictionary<,> and
- Error cloning dictionary ({source}) (last key was "{lastKey}
- Cannot create instance of type {type.FullName}. No parameter
- Cannot create instance of type {type.FullName} in AOT contex
- Cloning of immutable collections like {type.Name} is not sup
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/27e8c7bbad1a4a6f.
Report an issue: GitHub.