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
- Provide a non-empty integer on both sides of the colon, e.g. ";[2:1]" for 2 characters mapped to 1 glyph.
- If one of the sizes is 1, consider whether you still need the cluster form at all; a plain glyph index may suffice.
- 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
- Use 'n:1' explicitly when one side would be empty
- Format cluster specs from a helper method instead of string concatenation
- Add a regex check like '\(\d+:\d+\)' for cluster tokens
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
- SR.GlyphsClusterNoMatchingBracket
- SR.GlyphsIndexRequiredIfNoUnicode
- SR.GlyphsIndexRequiredWithinCluster
- SR.GlyphsTooManyCommas
- MappingParseError(_scanner.Start, MappingScanner.Ident…
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)