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
- 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).
- If you control a fork, add the converter type to the TryWriteWithKnownConverter allow-list in ScopeJsonConverter.cs so it is invoked without reflection.
- Disable NativeAOT/trimming for the app, or mark the assembly as trim-unfriendly, so RuntimeFeature.IsDynamicCodeSupported stays true and the dynamic path runs.
- 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
- Do not decorate ConfigurationProperty types with custom JsonConverters; register types with the source generator instead.
- Run a trimmed/AOT test build in CI that round-trips config to catch unregistered converters.
- Maintain the TryWriteWithKnownConverter allow-list if you fork Terminal.Gui.
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
- {propertyName}: Unsupported configuration converter type "{c
- {p.Key}: No source-generated JsonTypeInfo is registered for
- {propertyType.FullName}: No source-generated JsonTypeInfo is
- Unexpected StartObject token when parsing Attribute: {reader
- {propertyName}: Both Foreground and Background colors must b
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/fb4be352a1dce527.
Report an issue: GitHub.