dotnet/wpf · error · ArgumentException

SR.ClusterMapEntriesShouldNotDecrease

Error message

SR.ClusterMapEntriesShouldNotDecrease

What it means

GlyphRun.Initialize validates that the clusterMap (when non-null and non-empty) is monotonically non-decreasing: each entry must be >= the previous entry. A decreasing entry means the mapping from characters to glyph indices is inconsistent, so the library refuses to construct the GlyphRun. It throws ArgumentException naming the clusterMap parameter.

Solutions

  1. Sort/normalize the cluster map so entries are non-decreasing before constructing the GlyphRun
  2. Regenerate the clusterMap with a proper text shaper (e.g. GlyphTypeface/TextFormatter output) instead of hand-building it
  3. Ensure slices of clusterMap and characters/glyphIndices come from the same source run

Example fix

// before
int[] clusterMap = { 2, 1, 0 };
// after
int[] clusterMap = { 0, 1, 2 }; // non-decreasing entries
Defensive patterns

Strategy: validation

Validate before calling

if (clusterMap != null && clusterMap.Length > 0)
    for (int i = 1; i < clusterMap.Length; i++)
        if (clusterMap[i] < clusterMap[i-1]) throw new ArgumentException("clusterMap entries must not decrease");

Type guard

static bool IsValidClusterMap(int[] map, int glyphCount) =>
    map == null || map.Length == 0 ||
    (map[0] == 0 && map.All(v => v >= 0) && IsNonDecreasing(map) && map.All(v => v < glyphCount));

Try / catch

try { var run = new GlyphRun(...); }
catch (ArgumentException ex) when (ex.ParamName == "clusterMap") { /* rebuild map or log */ }

Prevention

When it happens

Trigger: Calling the GlyphRun constructor, GlyphRun.TryCreate, or EndInit (ISupportInitialize) with a non-null clusterMap array where some clusterMap[i] < clusterMap[i-1], e.g. building the map manually or reordering entries without resorting.

Common situations: Hand-rolling cluster maps when converting shaped text to GlyphRun; copying subset of clusters from another run but slicing the map at wrong offsets; mixing cluster maps from different shaping engines.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/GlyphRun.cs:384

                            // Perform some simple cluster map validation.
                            // First entry should be zero, the entries should be monotonic and shouldn't point outside of the glyph indices range.
                            if (clusterMap[0] == 0)
                            {
                                int glyphCount = GlyphCount;
                                int mapCount = clusterMap.Count;
                                ushort previous = clusterMap[0];

                                for (int i = 1; i < mapCount; ++i)
                                {
                                    ushort current = clusterMap[i];
                                    if ((current >= previous) && (current < glyphCount))
                                    {
                                        previous = current;
                                    }
                                    else
                                    {
                                        if (clusterMap[i] < clusterMap[i - 1])
                                            throw new ArgumentException(SR.ClusterMapEntriesShouldNotDecrease, nameof(clusterMap));

                                        if (clusterMap[i] >= GlyphCount)
                                            throw new ArgumentException(SR.ClusterMapEntryShouldPointWithinGlyphIndices, nameof(clusterMap));
                                    }
                                }
                            }
                            else
                            {
                                throw new ArgumentException(SR.ClusterMapFirstEntryMustBeZero, nameof(clusterMap));
                            }
                        }
                        else
                        {
                            throw new ArgumentException(SR.Format(SR.CollectionNumberOfElementsShouldBeEqualTo, characters.Count), nameof(clusterMap));
                        }
                    }
                    else
                    {

View on GitHub (pinned to 81131a70a4)