{"record":{"id":"27e8c7bbad1a4a6f","repo":"tui-cs/Terminal.Gui","slug":"cloning-of-frozen-or-immutable-dictionaries-like","errorCode":null,"errorMessage":"Cloning of frozen or immutable dictionaries like {type.Name} is not supported.","messagePattern":"Cloning of frozen or immutable dictionaries like (.+?) is not supported\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"Terminal.Gui/Configuration/DeepCloner.cs","lineNumber":459,"sourceCode":"            // Fallback to parameterless constructor if comparer constructor is not available\n            return (IDictionary)Activator.CreateInstance (dictType)!;\n        }\n    }\n\n    private static void CheckForUnsupportedDictionaryTypes (Type type)\n    {\n        Type? currentType = type;\n\n        while (currentType != null && currentType != typeof (object))\n        {\n            if (currentType.IsGenericType)\n            {\n                string? genericTypeName = currentType.GetGenericTypeDefinition ().FullName;\n\n                if (genericTypeName != null\n                    && (genericTypeName.StartsWith (\"System.Collections.Frozen\") || genericTypeName.StartsWith (\"System.Collections.Immutable\")))\n                {\n                    throw new NotSupportedException ($\"Cloning of frozen or immutable dictionaries like {type.Name} is not supported.\");\n                }\n            }\n\n            currentType = currentType.BaseType;\n        }\n    }\n\n    #endregion Dictionary Support\n\n    #region AOT Support\n\n    private static TScopeT CloneScope<TScopeT> (TScopeT scope, ConcurrentDictionary<object, object> visited)\n        where TScopeT : Scope<TScopeT>, new()\n    {\n        TScopeT clonedScope = new ();\n        visited.TryAdd (scope, clonedScope);\n\n        foreach (KeyValuePair<string, ConfigProperty> kvp in scope)","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/tui-cs/Terminal.Gui/blob/2e47b11478db083499917f2ad27c34d30efb0df1/Terminal.Gui/Configuration/DeepCloner.cs#L441-L477","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\npublic FrozenDictionary<string, Scheme> Schemes { get; set; }\n    = themes.ToFrozenDictionary ();\n\n// after — keep clonable storage mutable; freeze only at read time\npublic Dictionary<string, Scheme> Schemes { get; set; } = new (themes);\npublic FrozenDictionary<string, Scheme> SchemesFrozen => Schemes.ToFrozenDictionary ();","handlingStrategy":"validation","validationCode":"static bool IsClonableDictionary (object? obj)\n{\n    if (obj is not IDictionary) return false;\n    Type? t = obj.GetType ();\n    while (t is not null && t != typeof (object))\n    {\n        if (t.IsGenericType)\n        {\n            string? name = t.GetGenericTypeDefinition ().FullName;\n            if (name is not null\n                && (name.StartsWith (\"System.Collections.Frozen\")\n                    || name.StartsWith (\"System.Collections.Immutable\")))\n            {\n                return false;\n            }\n        }\n        t = t.BaseType;\n    }\n    return true;\n}\n\nif (!IsClonableDictionary (myDict))\n{\n    myDict = new Dictionary<,>(myDict); // thaw before cloning\n}","typeGuard":"static bool IsMutableDictionary (object obj)\n{\n    string? ns = obj.GetType ().Namespace;\n    return obj is IDictionary\n        && ns != \"System.Collections.Frozen\"\n        && ns != \"System.Collections.Immutable\";\n}","tryCatchPattern":"try\n{\n    var clone = DeepCloner.DeepClone (configWithDict);\n}\ncatch (NotSupportedException ex) when (ex.Message.Contains (\"frozen or immutable\"))\n{\n    // Convert to a mutable Dictionary<,> before assigning to the ConfigProperty.\n}","preventionTips":["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."],"tags":["deepcloner","dictionaries","immutable","frozen","configuration"],"backgroundTag":null,"analyzedSha":"2e47b11478db083499917f2ad27c34d30efb0df1","analyzedAt":"2026-08-13T19:20:08.826Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}