dotnet/wpf · error · InvalidOperationException

SR.Format(SR.TextSchema_CannotSplitElement…

Error message

SR.Format(SR.TextSchema_CannotSplitElement, nonMergeableAncestor.GetType().Name)

What it means

The Span(TextPointer start, TextPointer end) constructor throws InvalidOperationException with SR.TextSchema_CannotSplitElement (containing the element type name, e.g. 'Hyperlink') when either endpoint sits inside a non-mergeable inline element such as a Hyperlink. Creating the Span would require splitting that element, which the text schema forbids, so the constructor refuses regardless of argument order.

Solutions

  1. Move the start/end position outside the Hyperlink (e.g. to its ElementStart/ElementEnd) before constructing the Span.
  2. Check GetNonMergeableInlineAncestor() on both endpoints first and reject or adjust positions when non-null.
  3. Split the operation into ranges that exclude the hyperlink, or operate on the Hyperlink element itself instead of positions inside it.
  4. Catch InvalidOperationException and report that the position inside an un-splittable element (name in message) is not allowed.

Example fix

// before
var span = new Span(insideHyperlinkPointer, end);
// after
if (insideHyperlinkPointer.GetNonMergeableInlineAncestor() != null)
    insideHyperlinkPointer = insideHyperlinkPointer.GetNonMergeableInlineAncestor().ElementEnd;
var span = new Span(insideHyperlinkPointer, end);
Defensive patterns

Strategy: validation

Validate before calling

if (start.GetNonMergeableInlineAncestor() != null || end.GetNonMergeableInlineAncestor() != null)
    throw new InvalidOperationException("Range endpoints lie inside a non-mergeable inline element (e.g. Hyperlink).");

Type guard

static bool OutsideNonMergeableInline(TextPointer p) => p.GetNonMergeableInlineAncestor() == null;

Try / catch

try { span = new Span(start, end); }
catch (InvalidOperationException ex) when (ex.Message.Contains("split")) { /* move endpoints outside the element and retry */ }

Prevention

When it happens

Trigger: new Span(start, end) where start.GetNonMergeableInlineAncestor() (or the same for end) returns non-null - typically a position inside a Hyperlink (or other non-mergeable Inline) in a FlowDocument.

Common situations: Programmatically inserting/formatting content within a hyperlink in a RichTextBox/FlowDocument; building a Span whose endpoint lands inside UIElement-anchored or embedded inline content; text processing scripts that walk into hyperlinked text runs.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/Span.cs:116

            start.TextContainer.BeginChange();
            try
            {
                start = TextRangeEditTables.EnsureInsertionPosition(start);
                Invariant.Assert(start.Parent is Run);
                end = TextRangeEditTables.EnsureInsertionPosition(end);
                Invariant.Assert(end.Parent is Run);

                if (start.Paragraph != end.Paragraph)
                {
                    throw new ArgumentException(SR.Format(SR.InDifferentParagraphs, "start", "end"));
                }

                // If start or end positions have a Hyperlink ancestor, we cannot split them.
                Inline nonMergeableAncestor;
                if ((nonMergeableAncestor = start.GetNonMergeableInlineAncestor()) != null)
                {
                    throw new InvalidOperationException(SR.Format(SR.TextSchema_CannotSplitElement, nonMergeableAncestor.GetType().Name));
                }
                if ((nonMergeableAncestor = end.GetNonMergeableInlineAncestor()) != null)
                {
                    throw new InvalidOperationException(SR.Format(SR.TextSchema_CannotSplitElement, nonMergeableAncestor.GetType().Name));
                }

                TextElement commonAncestor = TextElement.GetCommonAncestor((TextElement)start.Parent, (TextElement)end.Parent);

                while (start.Parent != commonAncestor)
                {
                    start = SplitElement(start);
                }
                while (end.Parent != commonAncestor)
                {
                    end = SplitElement(end);
                }

                if (start.Parent is Run)

View on GitHub (pinned to 81131a70a4)