tui-cs/Terminal.Gui · error · ArgumentException

Only valid Unicode scalar values are allowed in a single Gra

Error message

Only valid Unicode scalar values are allowed in a single Grapheme cluster.

What it means

Thrown by the Cell.Grapheme setter when the value is a 1-character string whose single char is a Unicode surrogate (char.IsSurrogate returns true). A lone surrogate is not a valid Unicode scalar value; valid supplementary characters must be passed as a complete surrogate pair (which has length 2 and is one grapheme cluster, allowed by the prior check). This guards against corrupt strings produced by naive char-by-char slicing.

Source

Thrown at Terminal.Gui/Drawing/Cell.cs:37

    private string _grapheme = Grapheme;

    /// <summary>
    ///     The single grapheme cluster to display from this cell. If <see cref="Grapheme"/> is <see langword="null"/> or
    ///     <see cref="string.Empty"/>, then <see cref="Cell"/> is ignored.
    /// </summary>
    public string Grapheme
    {
        readonly get => _grapheme;
        set
        {
            if (GraphemeHelper.GetGraphemeCount (value) > 1)
            {
                throw new InvalidOperationException ($"Only a single {nameof (Grapheme)} cluster is allowed per Cell.");
            }

            if (!string.IsNullOrEmpty (value) && value.Length == 1 && char.IsSurrogate (value [0]))
            {
                throw new ArgumentException ($"Only valid Unicode scalar values are allowed in a single {nameof (Grapheme)} cluster.");
            }

            try
            {
                _grapheme = !string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)
                                ? value.Normalize (NormalizationForm.FormC)
                                : value;
            }
            catch (ArgumentException)
            {
                // leave text unnormalized
                _grapheme = value;
            }
        }
    }

    /// <summary>
    ///     The rune for <see cref="Grapheme"/> or runes for <see cref="Grapheme"/> that when combined makes this Cell a combining sequence.

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Enumerate graphemes (StringInfo, GraphemeHelper.GetGraphemes) or runes (str.EnumerateRunes()) instead of indexing chars, so supplementary characters stay intact.
  2. Validate with value.Length != 1 || !char.IsSurrogate(value[0]) before assigning.
  3. Use Cell.ToCellList to convert any string to cells safely regardless of encoding.

Example fix

// before - slices a surrogate pair
Cell c = new () { Grapheme = emoji [0].ToString () }; // throws if emoji is supplementary

// after
List<Cell> cells = Cell.ToCellList (emoji); // keeps the pair together as one Cell
Defensive patterns

Strategy: validation

Validate before calling

// Reject lone surrogates before assigning
if (value.Length == 1 && char.IsSurrogate (value [0]))
    throw new ArgumentException ("Use a complete surrogate pair / enumerate runes.");

Type guard

static bool IsValidScalar (string s) => !(s.Length == 1 && char.IsSurrogate (s [0]));

Prevention

When it happens

Trigger: Slicing a string that contains an emoji or CJK extension character into chars and assigning one surrogate half to a Cell — e.g. grapheme = str[someIndex].ToString() where str[someIndex] is a high or low surrogate.

Common situations: Using String.Substring/[] indexing on a string with supplementary-plane characters instead of a grapheme/rune-aware API; reading bytes from a mis-decoded stream; a faulty text editor that split a pair.

Related errors


AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13). Data as JSON: /api/errors/eb0a7c75d276c78c. Report an issue: GitHub.