dotnet/wpf · error · ArgumentException

SR.GlyphsClusterMisplacedSeparator

Error message

SR.GlyphsClusterMisplacedSeparator

What it means

Thrown by Glyphs.ReadGlyphIndex when a cluster spec "(chars:glyphs)" contains a ':' but it is misplaced — immediately after the opening bracket or immediately before the closing bracket — leaving an empty character- or glyph-cluster-size on one side of the colon. Both sides must be non-empty integers.

Solutions

  1. Provide a non-empty integer on both sides of the colon, e.g. ";[2:1]" for 2 characters mapped to 1 glyph.
  2. If one of the sizes is 1, consider whether you still need the cluster form at all; a plain glyph index may suffice.
  3. Pre-validate the token format (non-empty parts around ':') before assigning GlyphIndices.

Example fix

// before
glyphs.GlyphIndices = "[2:]"; // empty glyph cluster size
// after
glyphs.GlyphIndices = "[2:1]";
Defensive patterns

Strategy: validation

Validate before calling

static bool ClusterPartsNonEmpty(string token) { int o = token.IndexOf('('), c = token.IndexOf(')'), col = token.IndexOf(':'); return col < 0 || (o >= 0 && c > col && col > o + 1 && col < c - 1); }
if (!ClusterPartsNonEmpty(clusterToken)) throw new FormatException("Cluster must have non-empty parts around ':'");

Type guard

static bool HasEmptyClusterSide(string token) { int col = token.IndexOf(':'); return col >= 0 && (col == 0 || col == token.Length - 1 || token[col-1] == '(' || token[col+1] == ')'); }

Try / catch

try { glyphs.GlyphIndices = value; }
catch (ArgumentException ex) { throw new FormatException($"Invalid cluster separator in '{value}'", ex); }

Prevention

When it happens

Trigger: A GlyphIndices cluster token like "(;2)", "(2;)", or ";[:2]" / ";[2:]" where the colon is at position firstBracket+1 or secondBracket-1.

Common situations: Typos when hand-authoring GlyphIndices in XAML (empty side of the colon); generator code emitting empty cluster components for default values instead of omitting the colon.

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/1d94c26016214c30. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/Glyphs.cs:429

                    throw new ArgumentException(SR.GlyphsClusterNoNestedClusters);

                int secondBracket = valueSpec.IndexOf(')');
                if (secondBracket == -1 || secondBracket <= firstBracket + 1)
                    throw new ArgumentException(SR.GlyphsClusterNoMatchingBracket);

                // look for colon separator
                int colon = valueSpec.IndexOf(':');
                if (colon == -1)
                {
                    // parse glyph cluster size
                    ReadOnlySpan<char> characterClusterSpec = valueSpec.Slice(firstBracket + 1, secondBracket - (firstBracket + 1));
                    characterClusterSize = int.Parse(characterClusterSpec, provider: CultureInfo.InvariantCulture);
                    glyphClusterSize = 1;
                }
                else
                {
                    if (colon <= firstBracket + 1 || colon >= secondBracket - 1)
                        throw new ArgumentException(SR.GlyphsClusterMisplacedSeparator);
                    ReadOnlySpan<char> characterClusterSpec = valueSpec.Slice(firstBracket + 1, colon - (firstBracket + 1));
                    characterClusterSize = int.Parse(characterClusterSpec, provider: CultureInfo.InvariantCulture);
                    ReadOnlySpan<char> glyphClusterSpec = valueSpec.Slice(colon + 1, secondBracket - (colon + 1));
                    glyphClusterSize = int.Parse(glyphClusterSpec, provider: CultureInfo.InvariantCulture);
                }
                inCluster = true;
                glyphIndexString = valueSpec.Slice(secondBracket + 1);
            }
            if (IsEmpty(glyphIndexString))
                return false;

            glyphIndex = ushort.Parse(glyphIndexString, provider: CultureInfo.InvariantCulture);
            return true;
        }

        private static double GetAdvanceWidth(GlyphTypeface glyphTypeface, ushort glyphIndex, bool sideways)
        {
            double advance = sideways ? glyphTypeface.AdvanceHeights[glyphIndex] : glyphTypeface.AdvanceWidths[glyphIndex];

View on GitHub (pinned to 81131a70a4)