spectreconsole/spectre.console · error · InvalidOperationException

Encountered unescaped ']' token at position {_reader.Positio

Error message

Encountered unescaped ']' token at position {_reader.Position}

What it means

Inside MarkupTokenizer.ReadText, a literal `]` is only legal when immediately followed by another `]` (the `]]` escape for a literal close bracket). A lone `]` with any following character (or EOF) throws, reporting the reader position.

Source

Thrown at src/Spectre.Console.Ansi/AnsiMarkup.cs:333

        var position = _reader.Position;
        var builder = new StringBuilder();

        while (!_reader.Eof)
        {
            var current = _reader.Peek();
            if (current == '[')
            {
                // markup encountered. Stop processing.
                break;
            }

            // If we find a closing tag (']') there must be two of them.
            if (current == ']')
            {
                _reader.Read();
                if (_reader.Peek() != ']')
                {
                    throw new InvalidOperationException(
                        $"Encountered unescaped ']' token at position {_reader.Position}");
                }
            }

            builder.Append(_reader.Read());
        }

        return new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position);
    }

    private MarkupToken ReadMarkup()
    {
        var position = _reader.Position;

        // Read initial opening bracket
        _reader.Read();

        if (_reader.Eof)

View on GitHub (pinned to 0acc92fada)

Solutions

  1. Escape dynamic text with AnsiMarkup.Escape(text), which turns `]` into `]]` (and `[` into `[[`)
  2. Manually double any literal `]` in static markup strings
  3. Avoid splicing raw data into markup; render plain text separately

Example fix

// before
AnsiMarkup.Write($"Result: {userText}"); // breaks if userText contains ']'

// after
AnsiMarkup.Write($"Result: {AnsiMarkup.Escape(userText)}");
Defensive patterns

Strategy: validation

Validate before calling

// Escape dynamic text before embedding; this is the canonical prevention:
var safe = AnsiMarkup.Escape(userText);

Try / catch

try { AnsiMarkup.Write(text); }
catch (InvalidOperationException ex) when (ex.Message.Contains("unescaped ']'"))
{ AnsiMarkup.Write(AnsiMarkup.Escape(text)); }

Prevention

When it happens

Trigger: Markup text containing a single `]`, e.g. `"array[0]"`, `"a]b"`, or a trailing `]` at end of string. Note `[` alone in text is fine (it just breaks the text run); `]` is the one that must be doubled.

Common situations: Rendering user-typed or data-driven text that contains array indexing, regex, or JSON snippets without escaping; markdown/CSV content spliced into markup.

Related errors


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