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 pairs

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Use Dictionary<TKey,TValue> or ConcurrentDictionary<TKey,TValue> for any dictionary that will be deep-cloned by the configuration system.
  2. If ordering is needed, sort keys when reading rather than using SortedDictionary<,>.
  3. 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.
  4. 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.
  5. 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

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


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