{"record":{"id":"3107b06b476d5fd6","repo":"tui-cs/Terminal.Gui","slug":"expected-a-json-array-but-got-r","errorCode":null,"errorMessage":"Expected a JSON array (\"[ { ... } ]\"), but got \"{reader.TokenType}\".","messagePattern":"Expected a JSON array \\(\"\\[ (.+?) \\]\"\\), but got \"(.+?)\"\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"Terminal.Gui/Configuration/DictionaryJsonConverter.cs","lineNumber":17,"sourceCode":"﻿#nullable disable\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Terminal.Gui.Configuration;\n\ninternal class DictionaryJsonConverter<T> : JsonConverter<Dictionary<string, T>>\n{\n    public override Dictionary<string, T> Read (\n        ref Utf8JsonReader reader,\n        Type typeToConvert,\n        JsonSerializerOptions options\n    )\n    {\n        if (reader.TokenType != JsonTokenType.StartArray)\n        {\n            throw new JsonException ($\"Expected a JSON array (\\\"[ {{ ... }} ]\\\"), but got \\\"{reader.TokenType}\\\".\");\n        }\n\n        // If the Json options indicate ignoring case, use the invariant culture ignore case comparer.\n        Dictionary<string, T> dictionary = new (\n                                                options.PropertyNameCaseInsensitive\n                                                    ? StringComparer.InvariantCultureIgnoreCase\n                                                    : StringComparer.InvariantCulture);\n\n        while (reader.Read ())\n        {\n            if (reader.TokenType == JsonTokenType.StartObject)\n            {\n                reader.Read ();\n\n                if (reader.TokenType == JsonTokenType.PropertyName)\n                {\n                    string key = reader.GetString ();\n                    reader.Read ();","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/tui-cs/Terminal.Gui/blob/2e47b11478db083499917f2ad27c34d30efb0df1/Terminal.Gui/Configuration/DictionaryJsonConverter.cs#L1-L35","documentation":"Thrown by DictionaryJsonConverter<T>.Read (DictionaryJsonConverter.cs:15-18) when the JSON token is not StartArray. Terminal.Gui serializes Dictionary<string,T> config values as a JSON ARRAY of single-key objects (e.g. [{\"key\": value}, ...]), not as a JSON object. Feeding a conventional JSON object {\"key\": value} for such a property triggers this JsonException.","triggerScenarios":"A config JSON file (themes, app settings) has a dictionary-typed property written as a JSON object instead of the array-of-singletons form the converter expects. Also triggered by hand-editing config to the 'natural' object form, or by importing JSON produced by a different serializer that emits objects.","commonSituations":"User manually authors a theme JSON and writes \"Schemes\": { \"Base\": {...} } instead of \"Schemes\": [ { \"Base\": {...} } ]. A config migration tool emits standard object dictionaries. Copy-pasting a JSON snippet from docs that used the object form.","solutions":["Rewrite the dictionary property as a JSON array of single-property objects: [{\"Key1\": val1}, {\"Key2\": val2}].","Validate the config file shape against a Terminal.Gui config schema before loading.","Use ConfigurationManager to serialize a sample config once and use that output as the canonical format template.","If you control the producer, ensure it emits arrays for Dictionary<string,T> properties."],"exampleFix":"// before (wrong — object form)\n\"Schemes\": {\n  \"Base\": { \"Normal\": { \"Foreground\": \"White\" } }\n}\n\n// after (correct — array of singletons)\n\"Schemes\": [\n  { \"Base\": { \"Normal\": { \"Foreground\": \"White\" } } }\n]","handlingStrategy":"validation","validationCode":"// Validate that a Dictionary<string,T> property is a JSON array before deserializing\nusing System.Text.Json;\n\nstatic bool IsDictionaryPropertyArrayForm (string json, string propertyName)\n{\n    using JsonDocument doc = JsonDocument.Parse (json);\n    if (!doc.RootElement.TryGetProperty (propertyName, out JsonElement el)) return true; // absent is fine\n    return el.ValueKind == JsonValueKind.Array;\n}\n\nif (!IsDictionaryPropertyArrayForm (configJson, \"Schemes\"))\n{\n    // rewrite the property to array-of-singletons form before loading\n}","typeGuard":"static bool IsArrayForm (JsonElement el) => el.ValueKind == JsonValueKind.Array;","tryCatchPattern":"try\n{\n    ConfigurationManager.Load (configJson);\n}\ncatch (JsonException ex) when (ex.Message.Contains (\"Expected a JSON array\"))\n{\n    // The dictionary property was given as an object; convert it to\n    // [{\"key\": value}, ...] array form and reload.\n}","preventionTips":["Always emit Dictionary<string,T> config as an array of single-property objects.","Serialize a sample config with Terminal.Gui and use it as a template.","Validate config JSON with a schema before loading.","Document the array-of-singletons format for any team authoring config by hand."],"tags":["json","configuration","dictionary","serialization","config-file"],"backgroundTag":null,"analyzedSha":"2e47b11478db083499917f2ad27c34d30efb0df1","analyzedAt":"2026-08-13T19:20:08.826Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}