{"record":{"id":"8c242e4e810b4b40","repo":"microsoft/garnet","slug":"expected-start-of-json-object","errorCode":null,"errorMessage":"Expected start of JSON object.","messagePattern":"Expected start of JSON object\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"libs/host/Configuration/PopulateObjectJsonConverter.cs","lineNumber":32,"sourceCode":"    public class PopulateObjectJsonConverter<T> : JsonConverter<T> where T : class, new()\n    {\n        private readonly T existingInstance;\n\n        /// <summary>\n        /// Create a new converter instance\n        /// </summary>\n        /// <param name=\"existingInstance\">Instance to populate</param>\n        public PopulateObjectJsonConverter(T existingInstance)\n        {\n            this.existingInstance = existingInstance;\n        }\n\n        /// <inheritdoc />\n        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\n        {\n            if (reader.TokenType != JsonTokenType.StartObject)\n            {\n                throw new JsonException(\"Expected start of JSON object.\");\n            }\n\n            var jsonDocument = JsonDocument.ParseValue(ref reader);\n\n            // Only override properties that are specified in the source document\n            foreach (var property in jsonDocument.RootElement.EnumerateObject())\n            {\n                var propertyInfo = typeof(T).GetProperty(property.Name);\n                if (propertyInfo != null && propertyInfo.CanWrite)\n                {\n                    var propertyValue = JsonSerializer.Deserialize(property.Value.GetRawText(), propertyInfo.PropertyType, options);\n                    propertyInfo.SetValue(existingInstance, propertyValue);\n                }\n            }\n\n            return existingInstance;\n        }\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/host/Configuration/PopulateObjectJsonConverter.cs#L14-L50","documentation":"Thrown by PopulateObjectJsonConverter<T>.Read when the JSON reader is not positioned at a StartObject token ('{'). This converter is designed to merge a JSON object into an existing C# instance — it cannot process arrays, primitives, or top-level property fragments. The error is a contract violation: the caller fed non-object JSON where an object was required.","triggerScenarios":"Deserializing a JSON string that is a scalar, array, or whitespace/empty where the PopulateObjectJsonConverter<T> is registered. Commonly happens when a config override file contains a bare value (e.g., \"42\" or \"[1,2]\") instead of an object literal, or when the wrong converter is attached to a property.","commonSituations":"Garnet config override JSON files where a user writes a value instead of a key-value pair; programmatically calling JsonSerializer.Deserialize with a converter registered on a type but providing malformed JSON; feeding a JSON array where a settings merge object is expected.","solutions":["Ensure the JSON input begins with '{' and is a well-formed JSON object.","Validate the JSON with a parser/validator before passing it to the deserializer.","Check that PopulateObjectJsonConverter is registered against the correct type and the source stream is not truncated or corrupted."],"exampleFix":"// before: config file contains just a value\n//   6379\n\n// after: wrap in an object\n//   { \"Port\": 6379 }","handlingStrategy":"validation","validationCode":"using var doc = JsonDocument.Parse(jsonString);\nif (doc.RootElement.ValueKind != JsonValueKind.Object)\n    throw new JsonException($\"Expected a JSON object, got {doc.RootElement.ValueKind}.\");","typeGuard":"static bool IsJsonObject(string json)\n{\n    using var doc = JsonDocument.Parse(json);\n    return doc.RootElement.ValueKind == JsonValueKind.Object;\n}","tryCatchPattern":"try\n{\n    var result = JsonSerializer.Deserialize<T>(json, options);\n}\ncatch (JsonException ex) when (ex.Message.Contains(\"Expected start of JSON object\"))\n{\n    logger.LogError(\"Config override must be a JSON object, not a scalar or array\");\n    throw;\n}","preventionTips":["Always wrap config overrides in JSON object braces.","Validate JSON with a linter before feeding to the deserializer.","Use schema validation for user-supplied config files."],"tags":["json","deserialization","configuration","validation"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}