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
- 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.
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
- 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.
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
- {nameof(settings.LogSettings.NumberOfDeserializationBuffers)
- Invalid ClusterConfig payload: too short to contain a versio
- Unable to deserialize RedisOptions object. Line {lineCount}
- Unable to deserialize RedisOptions object. Unable to convert
- Unable to deserialize RedisOptions object. Unable to convert
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/8c242e4e810b4b40.
Report an issue: GitHub.