tui-cs/Terminal.Gui · error · JsonException

{p.Key}: Unsupported configuration converter type "{converte

Error message

{p.Key}: Unsupported configuration converter type "{converterType.FullName}" when dynamic code is unavailable.

What it means

Thrown in ScopeJsonConverter.Write when a configuration property carries a ConverterType (a property-level JsonConverter declared via the ConfigProperty) that is not one of the three hard-coded known converters (ConcurrentDictionaryJsonConverter<ThemeScope>, DictionaryJsonConverter<Scheme?>, TraceCategoryJsonConverter) AND the runtime does not support dynamic code (RuntimeFeature.IsDynamicCodeSupported is false). Under NativeAOT or a trimmed app, the reflective TryWriteWithDynamicConverter path is disabled, so an unregistered custom converter cannot be invoked and serialization aborts.

Source

Thrown at Terminal.Gui/Configuration/ScopeJsonConverter.cs:203

            {
                if (propertyValue is null)
                {
                    writer.WriteNullValue ();

                    continue;
                }

                if (TryWriteWithKnownConverter (writer, propertyType, converterType, propertyValue, options))
                {
                    continue;
                }

                if (TryWriteWithDynamicConverter (writer, propertyType, converterType, propertyValue, options))
                {
                    continue;
                }

                throw new JsonException ($"{p.Key}: Unsupported configuration converter type \"{converterType.FullName}\" when dynamic code is unavailable.");
            }
            object? prop = propertyValue;

            if (prop == null)
            {
                writer.WriteNullValue ();
            }
            else
            {
                if (TryWriteEnumValue (writer, prop.GetType (), prop))
                {
                    continue;
                }

                JsonTypeInfo? jsonTypeInfo = TuiSerializerContext.Instance.GetTypeInfo (prop.GetType ());

                if (jsonTypeInfo is null)
                {

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Remove the custom JsonConverter from the configuration property and rely on the source-generated TuiSerializerContext (register the property's type via [JsonSerializable] on SourceGenerationContext instead).
  2. If you control a fork, add the converter type to the TryWriteWithKnownConverter allow-list in ScopeJsonConverter.cs so it is invoked without reflection.
  3. Disable NativeAOT/trimming for the app, or mark the assembly as trim-unfriendly, so RuntimeFeature.IsDynamicCodeSupported stays true and the dynamic path runs.
  4. Avoid calling the serialization path at all (don't write config out) under AOT; only deserialize using registered types.

Example fix

// before
[JsonConverter (typeof (MyCustomSchemeConverter))]
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static Scheme? MyProp { get; set; }

// after - let the source generator handle it
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static Scheme? MyProp { get; set; }
// and register Scheme in SourceGenerationContext via [JsonSerializable(typeof(Scheme))]
Defensive patterns

Strategy: validation

Validate before calling

// Before publishing AOT, assert no config property uses an unregistered converter
if (!RuntimeFeature.IsDynamicCodeSupported)
{
    // Avoid writing config out, or ensure all ConverterTypes are in the known list:
    //   ConcurrentDictionaryJsonConverter<ThemeScope>, DictionaryJsonConverter<Scheme?>, TraceCategoryJsonConverter
}

Try / catch

try { string json = sourcesManager.ToJson (scope); }
catch (JsonException ex) when (ex.Message.Contains ("Unsupported configuration converter type"))
{ /* fall back to not serializing, or log and skip AOT publish */ }

Prevention

When it happens

Trigger: Publishing with dotnet publish -r <rid> /p:PublishAot=true (or with <PublishTrimmed>true</PublishTrimmed>) while a configuration property is annotated with a JsonConverter that is not in the known-converter allow-list. ConfigurationManager.Serialize/ToJson (or writing a config back out) then hits this arm because TryWriteWithKnownConverter and TryWriteWithDynamicConverter both return false.

Common situations: A library consumer adds a custom config property with [JsonConverter(typeof(MyConverter))] and ships an AOT build; upgrading Terminal.Gui in a previously-working AOT app if a new property introduces a new converter type not yet in the known list.

Related errors


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