tui-cs/Terminal.Gui · error · JsonException

Expected a JSON object ("{ "propName" : ... }"), but got "{r

Error message

Expected a JSON object ("{ "propName" : ... }"), but got "{reader.TokenType}".

What it means

ScopeJsonConverter.Read (used for SettingsScope and ThemeScope) expects a JSON object at the current position. If the token is not StartObject (e.g. an array, string, number, or true/false where a scope object is expected), it throws.

Source

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

/// <typeparam name="TScopeT"></typeparam>
internal class ScopeJsonConverter<
    [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor
                                 | DynamicallyAccessedMemberTypes.PublicProperties)]
TScopeT> : JsonConverter<TScopeT> where TScopeT : Scope<TScopeT>
{
    [UnconditionalSuppressMessage ("AOT",
                                   "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.",
                                   Justification =
                                       "Arbitrary property-level converter fallback is guarded by RuntimeFeature.IsDynamicCodeSupported and is unreachable under NativeAOT.")]
    [UnconditionalSuppressMessage ("Trimming",
                                   "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
                                   Justification =
                                       "Arbitrary property-level converter fallback is only used when a consumer opts into a custom property-level JsonConverter on JIT-supported runtimes.")]
    public override TScopeT Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException ($$"""Expected a JSON object ("{ "propName" : ... }"), but got "{{reader.TokenType}}".""");
        }

        var scope = (TScopeT)Activator.CreateInstance (typeof (TScopeT))!;
        var propertyName = string.Empty;

        while (reader.Read ())
        {
            if (reader.TokenType == JsonTokenType.EndObject)
            {
                return scope!;
            }

            if (reader.TokenType != JsonTokenType.PropertyName)
            {
                throw new JsonException ($"After {propertyName}: Expected a JSON property name, but got \"{reader.TokenType}\"");
            }

            propertyName = reader.GetString ();

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Ensure the config root and each theme entry are JSON objects.
  2. Validate the document structure against the expected schema.

Example fix

// before
[ { "Theme": { ... } } ]
// after
{ "Themes": [ { ... } ] }
Defensive patterns

Strategy: validation

Validate before calling

using var doc = JsonDocument.Parse (configText);
if (doc.RootElement.ValueKind != JsonValueKind.Object) return false;

Try / catch

try { ConfigurationManager.Apply (); }
catch (JsonException ex) when (ex.Message.Contains ("Expected a JSON object"))
{ /* the token type in ex.Message shows what was found instead */ }

Prevention

When it happens

Trigger: The top-level config or a theme entry is mapped to a non-object, e.g. the whole config file is a JSON array, or a theme value is a bare string instead of an object.

Common situations: Malformed config file structure; wrong root element; tooling emitting arrays where objects are expected.

Related errors


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