elsa-workflows/elsa-core · error · InvalidOperationException
The Platform shell overlay file must contain a JSON object.
Error message
The Platform shell overlay file must contain a JSON object.
What it means
FileShellConfigurationOverlayStore.LoadAsync reads the shell overlay file and parses it with JsonNode.ParseAsync, then casts to JsonObject. If the file's top-level JSON value is not an object (e.g. an array, string, or number), the cast fails and this InvalidOperationException is thrown.
Solutions
- Edit the overlay file so its root is a JSON object, e.g. { }. Validate with a JSON parser.
- Delete or rename the malformed file; LoadAsync recreates an empty JsonObject when the file is absent.
- Restore the file from backup or regenerate it by re-saving the overlay through the integration.
Example fix
// before (shell-overlay.json)
[]
// after (shell-overlay.json)
{} Defensive patterns
Strategy: try-catch
Validate before calling
using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(overlayPath));
if (doc.RootElement.ValueKind != JsonValueKind.Object)
throw new InvalidOperationException("Overlay file root must be a JSON object."); Type guard
bool IsValidOverlayRoot(JsonNode? node) => node is JsonObject;
Try / catch
try { var root = await store.LoadAsync(ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must contain a JSON object"))
{
logger.LogWarning(ex, "Overlay file malformed; starting from empty overlay.");
root = new JsonObject();
} Prevention
- Never hand-edit the overlay into a non-object root; validate JSON after manual edits.
- Restrict write access to the overlay file to the application.
- Add a startup sanity check that parses the overlay and reports schema problems early.
When it happens
Trigger: The file at ShellOverlayPath exists and contains valid JSON whose root is not an object — for example '[ ]' or '"text"' — and LoadAsync is invoked during overlay load.
Common situations: A user hand-edited the overlay file into an array; an export tool wrote a different JSON shape; the file was overwritten by another process with the wrong schema.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Cannot convert to bool
- Cannot convert to bool?
- Cannot deserialize to .
- Cannot determine the element type of array
- Expected a metadata property name.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/5ae1afcca9607a94.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Platform.Integration/Services/FileShellConfigurationOverlayStore.cs:82
Merge(configuration, settingsObject);
return await SaveIfChangedAsync(root, before, cancellationToken);
}
finally
{
_semaphore.Release();
}
}
private async Task<JsonObject> LoadAsync(CancellationToken cancellationToken)
{
var path = GetPath();
if (!File.Exists(path))
return new JsonObject();
await using var stream = File.OpenRead(path);
return await JsonNode.ParseAsync(stream, cancellationToken: cancellationToken) as JsonObject
?? throw new InvalidOperationException("The Platform shell overlay file must contain a JSON object.");
}
private async Task<bool> SaveIfChangedAsync(JsonObject root, string before, CancellationToken cancellationToken)
{
var after = Serialize(root);
if (string.Equals(before, after, StringComparison.Ordinal))
return false;
var path = GetPath();
var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directory))
Directory.CreateDirectory(directory);
var tempPath = $"{path}.{Guid.NewGuid():N}.tmp";
await File.WriteAllTextAsync(tempPath, after, cancellationToken);
File.Move(tempPath, path, overwrite: true);
return true;
}View on GitHub (pinned to fe9217bdfa)