dotnet/wpf · error · XamlParseException

SR.InvalidClosingBracketCharacers

Error message

SR.InvalidClosingBracketCharacers

What it means

MeScanner.ReadString tracks a stack of open bracket characters inside a markup-extension string. When a closing bracket arrives (e.g. ')' or ']') that does not match the bracket currently on top of the stack, the scanner throws XamlParseException with SR.InvalidClosingBracketCharacers, naming the offending character. This keeps brace/bracket nesting inside `{}` values unambiguous so System.Xaml can tell extension boundaries from literal text.

Solutions

  1. Balance the brackets in the value so every closer matches the most recently opened bracket.
  2. Escape literal braces with a leading {} sequence if the text is not meant to be a markup extension.
  3. Move bracket-heavy literal values into a resource (resx/ResourceDictionary) and reference them, avoiding inline parsing.
  4. Check the reported character and its position in the attribute value; remove or escape the stray closer.

Example fix

<!-- before -->
<TextBox Text="{StaticResource Pattern)]}" />

<!-- after -->
<TextBox Text="{StaticResource Pattern}" />
Defensive patterns

Strategy: validation

Validate before calling

static bool BracketsBalanced(string value) { int depth = 0; foreach (var c in value) { if (c=='('||c=='[') depth++; if (c==')'||c==']') depth--; if (depth<0) return false; } return depth==0; }

Try / catch

try { return XamlReader.Parse(xaml); } catch (XamlParseException ex) when (ex.Message.Contains("bracket")) { return null; }

Prevention

When it happens

Trigger: Parsing XAML where a markup extension string contains a closing bracket without its matching opener, e.g. `Text="{StaticResource Key)]}"` or nested `{x:Static Member=...)}` where a ')' appears with only '[' opened.

Common situations: Copy-pasted XAML where literal ')' text was not escaped; template or code-generated markup with mismatched bracket pairs; values like regular expressions or format strings containing brackets pasted into markup extensions unescaped.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/6096bb28eb33bc7b. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/Parser/MeScanner.cs:400

                // If we are inside of MarkupExtensionBracketCharacters for a particular property or position parameter,
                // scoop up everything inside one by one, and keep track of nested Bracket Characters in the stack.
                else if (_context.CurrentBracketModeParseParameters is not null && _context.CurrentBracketModeParseParameters.IsBracketEscapeMode)
                {
                    Stack<char> bracketCharacterStack = _context.CurrentBracketModeParseParameters.BracketCharacterStack;
                    if (_currentSpecialBracketCharacters.StartsEscapeSequence(ch))
                    {
                        bracketCharacterStack.Push(ch);
                    }
                    else if (_currentSpecialBracketCharacters.EndsEscapeSequence(ch))
                    {
                        if (_currentSpecialBracketCharacters.Match(bracketCharacterStack.Peek(), ch))
                        {
                            bracketCharacterStack.Pop();
                        }
                        else
                        {
                            throw new XamlParseException(this, SR.Format(SR.InvalidClosingBracketCharacers, ch.ToString()));
                        }
                    }
                    else if (ch == Backslash)
                    {
                        escaped = true;
                    }

                    if (bracketCharacterStack.Count == 0)
                    {
                        _context.CurrentBracketModeParseParameters.IsBracketEscapeMode = false;
                    }

                    if (!escaped)
                    {
                        sb.Append(ch);
                    }
                }
                else

View on GitHub (pinned to 81131a70a4)