OrchardCMS/OrchardCore · error · FormatException
Top-level JSON element must be an object. Instead
Error message
Top-level JSON element must be an object. Instead, '{doc.RootElement.ValueKind}' was found. What it means
JsonConfigurationParser.Parse(Stream) flattens a JSON appsettings-like document into configuration key/value pairs, and it requires the top-level JSON element to be an object ({}) because keys are built from property paths. If the root is an array, string, number, or any other JSON value kind, a FormatException is thrown naming the actual ValueKind found.
Solutions
- Wrap the document's top level in a JSON object, e.g. change '[1,2,3]' to '{"values": [1,2,3]}'
- Inspect the file/stream contents and restore a valid appsettings object with key:value properties
- Validate the JSON root before parsing: parse with System.Text.Json and check JsonElement.ValueKind == JsonValueKind.Object
- If parsing tenant appsettings, restore the file from source control or re-save tenant settings through the admin UI
Example fix
// before
["Default", "MyTenant"]
// after
{"tenants": ["Default", "MyTenant"]} Defensive patterns
Strategy: validation
Validate before calling
using var doc = JsonDocument.Parse(stream, JOptions.Document);
if (doc.RootElement.ValueKind != JsonValueKind.Object)
throw new InvalidDataException($"Expected a JSON object, got '{doc.RootElement.ValueKind}'.");
stream.Position = 0; // rewind before the real parse
var data = JsonConfigurationParser.Parse(stream); Type guard
static bool IsJsonObject(Stream s) { using var doc = JsonDocument.Parse(s, JOptions.Document); return doc.RootElement.ValueKind == JsonValueKind.Object; } Try / catch
try { JsonConfigurationParser.Parse(stream); }
catch (FormatException ex) { log.LogError(ex, "JSON root must be an object"); } Prevention
- Check the first non-whitespace character of the payload is '{'
- Keep appsettings files as objects, never arrays or scalars
- Validate JSON after any scripted write to config files
- Back up appsettings.json before tooling edits
When it happens
Trigger: Calling JsonConfigurationParser.Parse(Stream) with a stream whose JSON root is not an object, e.g. a stream containing '[1,2,3]', '"text"', '42', 'true', or 'null'. Also occurs when a tenant's appsettings file was accidentally overwritten with a JSON array or scalar.
Common situations: A shell appsettings.json (e.g. App_Data/Sites/{tenant}/appsettings.json) truncated or replaced with a non-object payload; a tool exporting settings as a JSON array; someone piping the wrong file (e.g. a JSON log or a package.json 'dependencies' fragment) into configuration parsing.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Top level JSON element must be an object. Instead
- Can't use the numeric key
- Can't use the non numeric key
- Could not parse the JSON document.
- A duplicate key ' ' was found.
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/f2399ffc5afc3627.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.Abstractions/Shell/Configuration/Internal/JsonConfigurationParser.cs:33
public static IDictionary<string, string?> Parse(Stream utf8Json)
=> new JsonConfigurationParser().ParseStream(utf8Json);
public static IDictionary<string, string?> Parse(string document)
=> new JsonConfigurationParser().ParseDocument(document);
public static Task<IDictionary<string, string?>> ParseAsync(Stream utf8Json)
=> new JsonConfigurationParser().ParseStreamAsync(utf8Json);
private Dictionary<string, string?> ParseStream(Stream utf8Json)
{
try
{
using (var doc = JsonDocument.Parse(utf8Json, JOptions.Document))
{
if (doc.RootElement.ValueKind != JsonValueKind.Object)
{
throw new FormatException($"Top-level JSON element must be an object. Instead, '{doc.RootElement.ValueKind}' was found.");
}
VisitObjectElement(doc.RootElement);
}
return _data;
}
catch (JsonException e)
{
throw new FormatException("Could not parse the JSON document.", e);
}
}
private Dictionary<string, string?> ParseDocument(string document)
{
try
{
using (var doc = JsonDocument.Parse(document, JOptions.Document))View on GitHub (pinned to 4306c0717f)