spectreconsole/spectre.console · error · InvalidOperationException
Encountered unescaped ']' token at position
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.
Solutions
- Escape dynamic text with AnsiMarkup.Escape(text), which turns `]` into `]]` (and `[` into `[[`)
- Manually double any literal `]` in static markup strings
- 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
- Never interpolate raw data into markup; always run it through AnsiMarkup.Escape
- Remember `]` is the dangerous char (must be `]]`), more so than `[`
- Treat markup strings like a DSL, not a format string
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
- A background color has already been set.
- A foreground color has already been set.
- A link has already been set.
- Color number must be greater than or equal to 0
- Color number must be less than or equal to 255
AI-assisted analysis of spectreconsole/spectre.console@0acc92fada (2026-08-13).
Data as JSON: /api/errors/c14da017bf50a204.
Report an issue: GitHub.
Appendix: 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)