tui-cs/Terminal.Gui · error · NotSupportedException

Cloning of collection type {type.Name} is not supported with

Error message

Cloning of collection type {type.Name} is not supported without a parameterless constructor.

What it means

Thrown by DeepCloner.CloneCollection when an ICollection-implementing, IList-implementing collection type cannot be instantiated as an IList via Activator.CreateInstance(type). The cloner refuses to clone collections it cannot construct with a parameterless constructor, because it needs an empty destination list to copy cloned elements into. This is a hard guard at DeepCloner.cs:228-231.

Source

Thrown at Terminal.Gui/Configuration/DeepCloner.cs:230

        // Check for immutable collections and throw if found
        if (type.IsGenericType)
        {
            Type genericTypeDef = type.GetGenericTypeDefinition ();

            if (genericTypeDef.FullName != null && genericTypeDef.FullName.StartsWith ("System.Collections.Immutable"))
            {
                throw new NotSupportedException ($"Cloning of immutable collections like {type.Name} is not supported.");
            }
        }

        if (source is not IList)
        {
            throw new NotSupportedException ($"Cloning of collection type {type.Name} is not supported unless it implements IList.");
        }

        if (Activator.CreateInstance (type) is not IList tempList)
        {
            throw new NotSupportedException ($"Cloning of collection type {type.Name} is not supported without a parameterless constructor.");
        }

        // Add to visited before cloning contents to prevent circular reference issues
        visited.TryAdd (source, tempList);

        foreach (object? item in (IEnumerable)source)
        {
            object? clonedItem = DeepCloneInternal (item, visited);
            tempList.Add (clonedItem);
        }

        return tempList;
    }

    #region Dictionary Support

    [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Dictionary cloning constructs supported runtime dictionary shapes (Dictionary<,> and ConcurrentDictionary<,>) via MakeGenericType, which is validated by NativeAOT publish tests.")]
    [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = "Dictionary cloning reads the runtime dictionary comparer from supported dictionary types to preserve comparer semantics.")]

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Replace the custom collection type with List<T> (which has a parameterless constructor) for any property that participates in a configuration scope.
  2. Add a public parameterless constructor to the custom collection type so Activator.CreateInstance can build it.
  3. Ensure the collection type implements IList (not just ICollection/IEnumerable); types implementing only ICollection and IEnumerable but not IList hit the earlier guard at line 223-226, not this one.
  4. If running under NativeAOT, register the collection type in a JsonSerializerContext (SourceGenerationContext) so the AOT fallback path in CreateInstance/DeepCloneInternal can construct it.
  5. Avoid storing immutable or frozen collections in clonable config; convert to List<T> before assigning to a ConfigProperty.

Example fix

// before
public class MyScope
{
    public ReadOnlyCollection<int> Items { get; set; } // no parameterless ctor, not IList-creatable
}

// after
public class MyScope
{
    public List<int> Items { get; set; } = new ();
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Before cloning, verify the collection type is IList-constructible
static bool IsClonableCollection (object? obj)
{
    if (obj is not IList) return false;
    Type t = obj.GetType ();
    return t.GetConstructor (Type.EmptyTypes) != null
        && typeof (IList).IsAssignableFrom (t);
}

// Usage
if (!IsClonableCollection (myConfigProperty))
{
    myConfigProperty = new List<...> (...); // normalize to List<T>
}

Type guard

static bool IsClonableCollection<T> (T value) where T : notnull
{
    Type t = value.GetType ();
    return value is IList
        && t.GetConstructor (Type.EmptyTypes) is not null
        && !t.FullName!.StartsWith ("System.Collections.Immutable");
}

Try / catch

try
{
    var clone = DeepCloner.DeepClone (configObject);
}
catch (NotSupportedException ex) when (ex.Message.Contains ("parameterless constructor"))
{
    // Normalize the offending collection to List<T> and retry,
    // or report which property holds the unsupported type.
}

Prevention

When it happens

Trigger: DeepClone is called on an object graph that transitively contains an ICollection whose runtime type (1) has no public parameterless constructor, or (2) returns a non-IList instance from Activator.CreateInstance (e.g. a Nullable<T>-boxed struct, or a collection whose default ctor yields a type not assignable to IList). In practice this most often surfaces under NativeAOT/trimming where Activator.CreateInstance returns null for trimmed constructors before this line, or for custom collection types with only parameterized constructors.

Common situations: A user stores a custom collection type (with only an int-capacity or IEqualityComparer constructor) inside a configuration scope property; ConfigurationManager applies the scope and triggers a deep clone. Also seen after upgrading to a Terminal.Gui version that deep-clones more aggressively, exposing a previously-uncloned custom collection.

Related errors


AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13). Data as JSON: /api/errors/ec26c6b624c47f16. Report an issue: GitHub.