spectreconsole/spectre.console · error · InvalidOperationException

Invalid JSON

Error message

Invalid JSON

What it means

JsonParser.Shared.Parse(string) tokenizes and reads JSON; any exception from the tokenizer or reader is swallowed and re-thrown as a bare InvalidOperationException("Invalid JSON") with no inner exception, so the original cause (unexpected token, trailing comma, unterminated string) is lost.

Source

Thrown at src/Extensions/Spectre.Console.Json/JsonParser.cs:17

namespace Spectre.Console.Json;

internal sealed class JsonParser : IJsonParser
{
    public static JsonParser Shared { get; } = new JsonParser();

    public JsonSyntax Parse(string json)
    {
        try
        {
            var tokens = JsonTokenizer.Tokenize(json);
            var reader = new JsonTokenReader(tokens);
            return ParseElement(reader);
        }
        catch
        {
            throw new InvalidOperationException("Invalid JSON");
        }
    }

    private static JsonSyntax ParseElement(JsonTokenReader reader)
    {
        return ParseValue(reader);
    }

    private static List<JsonSyntax> ParseElements(JsonTokenReader reader)
    {
        var members = new List<JsonSyntax>();

        while (!reader.Eof)
        {
            members.Add(ParseElement(reader));

            if (reader.Peek()?.Type != JsonTokenType.Comma)
            {

View on GitHub (pinned to 0acc92fada)

Solutions

  1. Validate/parse the JSON with System.Text.Json.JsonDocument.Parse first to get a precise error location
  2. Wrap the call in try/catch and log the raw input plus its length for diagnosis
  3. Fix the source JSON (close braces, quote keys, remove trailing commas)
  4. If you control input generation, round-trip it through a strict serializer before storing

Example fix

// before
var syntax = JsonParser.Shared.Parse(maybeBrokenJson);

// after (pre-validate to surface the real error)
try
{
    using var doc = JsonDocument.Parse(maybeBrokenJson);
}
catch (JsonException ex)
{
    throw new InvalidOperationException($"Input is not valid JSON at line {ex.LineNumber}, pos {ex.BytePositionInLine}.", ex);
}
var syntax = JsonParser.Shared.Parse(maybeBrokenJson);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate JSON with the runtime serializer to get a precise error:
public static bool IsValidJson(string input)
{
    try { using var _ = JsonDocument.Parse(input); return true; }
    catch (JsonException) { return false; }
}

Try / catch

try
{
    var syntax = JsonParser.Shared.Parse(json);
}
catch (InvalidOperationException ex) when (ex.Message == "Invalid JSON")
{
    // Log raw input + length; the library discards the inner cause,
    // so fall back to JsonDocument.Parse for diagnostics if needed.
    logger.LogWarning("Rejected malformed JSON (len={Len}).", json.Length);
}

Prevention

When it happens

Trigger: Calling JsonParser.Shared.Parse(json) (or any code path that uses it) with a malformed JSON string: unbalanced braces, trailing commas, unquoted keys, truncated input, or BOM/encoding artifacts.

Common situations: Feeding user-supplied or file-loaded JSON without pre-validation; a config file edited by hand with a syntax error; an upstream service changed its payload format; copy-paste that dropped a closing brace.

Understand the failure class

Related errors


AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13). Data as JSON: /api/errors/ba4e1c3fca8846f4. Report an issue: GitHub.