microsoft/garnet · error · JsonException

Expected start of JSON object.

Error message

Expected start of JSON object.

What it means

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.

Source

Thrown at libs/host/Configuration/PopulateObjectJsonConverter.cs:32

    public class PopulateObjectJsonConverter<T> : JsonConverter<T> where T : class, new()
    {
        private readonly T existingInstance;

        /// <summary>
        /// Create a new converter instance
        /// </summary>
        /// <param name="existingInstance">Instance to populate</param>
        public PopulateObjectJsonConverter(T existingInstance)
        {
            this.existingInstance = existingInstance;
        }

        /// <inheritdoc />
        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException("Expected start of JSON object.");
            }

            var jsonDocument = JsonDocument.ParseValue(ref reader);

            // Only override properties that are specified in the source document
            foreach (var property in jsonDocument.RootElement.EnumerateObject())
            {
                var propertyInfo = typeof(T).GetProperty(property.Name);
                if (propertyInfo != null && propertyInfo.CanWrite)
                {
                    var propertyValue = JsonSerializer.Deserialize(property.Value.GetRawText(), propertyInfo.PropertyType, options);
                    propertyInfo.SetValue(existingInstance, propertyValue);
                }
            }

            return existingInstance;
        }

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Ensure the JSON input begins with '{' and is a well-formed JSON object.
  2. Validate the JSON with a parser/validator before passing it to the deserializer.
  3. Check that PopulateObjectJsonConverter is registered against the correct type and the source stream is not truncated or corrupted.

Example fix

// before: config file contains just a value
//   6379

// after: wrap in an object
//   { "Port": 6379 }
Defensive patterns

Strategy: validation

Validate before calling

using var doc = JsonDocument.Parse(jsonString);
if (doc.RootElement.ValueKind != JsonValueKind.Object)
    throw new JsonException($"Expected a JSON object, got {doc.RootElement.ValueKind}.");

Type guard

static bool IsJsonObject(string json)
{
    using var doc = JsonDocument.Parse(json);
    return doc.RootElement.ValueKind == JsonValueKind.Object;
}

Try / catch

try
{
    var result = JsonSerializer.Deserialize<T>(json, options);
}
catch (JsonException ex) when (ex.Message.Contains("Expected start of JSON object"))
{
    logger.LogError("Config override must be a JSON object, not a scalar or array");
    throw;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/8c242e4e810b4b40. Report an issue: GitHub.