{"record":{"id":"8770d868d2e25150","repo":"dotnet/wpf","slug":"sr-glyphsclusternomatchingbracket","errorCode":null,"errorMessage":"SR.GlyphsClusterNoMatchingBracket","messagePattern":"SR\\.GlyphsClusterNoMatchingBracket","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/Glyphs.cs","lineNumber":415,"sourceCode":"            // the format is ... [(CharacterClusterSize[:GlyphClusterSize])] GlyphIndex ...\n            ReadOnlySpan<char> glyphIndexString = valueSpec;\n\n            int firstBracket = valueSpec.IndexOf('(');\n            if (firstBracket != -1)\n            {\n                // Only spaces are allowed before the bracket\n                for (int i=0; i<firstBracket; i++)\n                {\n                    if (!Char.IsWhiteSpace(valueSpec[i]))\n                        throw new ArgumentException(SR.GlyphsClusterBadCharactersBeforeBracket);\n                }\n\n                if (inCluster)\n                    throw new ArgumentException(SR.GlyphsClusterNoNestedClusters);\n\n                int secondBracket = valueSpec.IndexOf(')');\n                if (secondBracket == -1 || secondBracket <= firstBracket + 1)\n                    throw new ArgumentException(SR.GlyphsClusterNoMatchingBracket);\n\n                // look for colon separator\n                int colon = valueSpec.IndexOf(':');\n                if (colon == -1)\n                {\n                    // parse glyph cluster size\n                    ReadOnlySpan<char> characterClusterSpec = valueSpec.Slice(firstBracket + 1, secondBracket - (firstBracket + 1));\n                    characterClusterSize = int.Parse(characterClusterSpec, provider: CultureInfo.InvariantCulture);\n                    glyphClusterSize = 1;\n                }\n                else\n                {\n                    if (colon <= firstBracket + 1 || colon >= secondBracket - 1)\n                        throw new ArgumentException(SR.GlyphsClusterMisplacedSeparator);\n                    ReadOnlySpan<char> characterClusterSpec = valueSpec.Slice(firstBracket + 1, colon - (firstBracket + 1));\n                    characterClusterSize = int.Parse(characterClusterSpec, provider: CultureInfo.InvariantCulture);\n                    ReadOnlySpan<char> glyphClusterSpec = valueSpec.Slice(colon + 1, secondBracket - (colon + 1));\n                    glyphClusterSize = int.Parse(glyphClusterSpec, provider: CultureInfo.InvariantCulture);","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/Glyphs.cs#L397-L433","documentation":"Thrown by Glyphs.ReadGlyphIndex while parsing a glyph cluster spec like \"[3:2]\" in the Glyphs.GlyphIndices string. When an opening bracket '(' was seen, a matching ')' must exist and must leave room for at least one character between the brackets. The parser throws ArgumentException when the closing bracket is missing or directly abuts the opening one.","triggerScenarios":"Setting Glyphs.GlyphIndices (directly or via ParseGlyphsProperty, e.g. from XAML) with a cluster token containing '(' but no ')', or a token like \"()[...]\" where secondBracket == firstBracket + 1 (empty cluster spec).","commonSituations":"Hand-written GlyphIndices strings in XAML with a truncated or typoed cluster section (e.g. \";[2\" or \";()\"); string generation code that forgot the closing bracket; copy/paste truncation.","solutions":["Add the matching ')' and at least one character inside the cluster spec, e.g. \";[3:2]\" or \"(12:2)\".","Validate the GlyphIndices string with a parser or regex before assigning it to Glyphs.GlyphIndices.","Wrap the property assignment in try/catch for ArgumentException to surface a clearer message to the user."],"exampleFix":"// before\nglyphs.GlyphIndices = \"10;[2;20\"; // missing ')'\n// after\nglyphs.GlyphIndices = \"10;[2:2];20\";","handlingStrategy":"validation","validationCode":"static bool HasBalancedNonEmptyCluster(string indices) => indices == null || indices.Split(';').All(s => !s.Contains('(') || (s.Contains(')') && s.IndexOf(')') > s.IndexOf('(') + 1));\nif (!HasBalancedNonEmptyCluster(glyphIndices)) throw new FormatException(\"Malformed glyph cluster spec\");\nglyphs.GlyphIndices = glyphIndices;","typeGuard":"static bool IsValidClusterToken(string token) => !token.Contains('(') || (token.Contains(')') && token.IndexOf(')') > token.IndexOf('(') + 1);","tryCatchPattern":"try { glyphs.GlyphIndices = value; }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"bracket\")) { LogParseFailure(value); throw new FormatException(\"GlyphIndices cluster spec missing matching bracket\", ex); }","preventionTips":["Always emit clusters as '(n:m)' with both brackets present","Unit-test GlyphIndices strings through a parser before shipping generated output","Never hand-edit cluster sections without checking bracket pairing"],"tags":["wpf","glyphs","parsing","xaml","argumentexception"],"backgroundTag":"invalid-argument-format","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}