dotnet/wpf · error · InvalidOperationException

SR.TextSchema_IllegalHyperlinkChild (nextElement.GetType())

Error message

SR.TextSchema_IllegalHyperlinkChild (nextElement.GetType())

What it means

HasIllegalHyperlinkDescendant scans a position range for Hyperlink or AnchoredBlock elements that would illegally descend from a hyperlink. If one is found and throwIfIllegalDescendent is true, an InvalidOperationException is thrown naming the offending element's type. It enforces the rule that Hyperlink content cannot contain nested Hyperlinks or block-level AnchoredBlocks.

Solutions

  1. Before applying a Hyperlink over a range, remove/flatten existing nested Hyperlink elements within the selection
  2. Split the enclosing Hyperlink so the edited range lies outside any existing link scope (TextRangeEdit helpers do this; replicate it for custom code)
  3. Replace illegal AnchoredBlock descendants with inline equivalents (Run/Span) inside link content
  4. Catch InvalidOperationException, locate the nested element via the message type, and restructure the tree before retrying the edit

Example fix

// before
TextRange selection = new TextRange(start, end); // selection contains an existing Hyperlink
selection.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Blue); // edit crossing Hyperlink boundary -> HasIllegalHyperlinkDescendant throws when creating nested link
// after
foreach (var hl in FindHyperlinks(selection)) FlattenHyperlink(hl); // unwrap nested links first
selection.ApplyPropertyValue(...);
Defensive patterns

Strategy: validation

Validate before calling

static bool RangeHasIllegalLinkDescendant(TextPointer start, TextPointer end) => /* scan with GetNextContextPosition; throw-free probe via throwIfIllegalDescendent:false */ HasIllegalHyperlinkDescendant(start, end, false);

Type guard

static bool IsSafeLinkContent(TextElement e) => !(e is Hyperlink) && !(e is AnchoredBlock);

Try / catch

try { ApplyLinkFormatting(range); } catch (InvalidOperationException ex) when (ex.Message.Contains("IllegalHyperlinkChild")) { SplitEnclosingHyperlinks(range); ApplyLinkFormatting(range); }

Prevention

When it happens

Trigger: Text edits (e.g. applying Hyperlink formatting over a selection, TextRange save/paste, splitting operations) that would leave a Hyperlink or AnchoredBlock (Paragraph/Table/List/Floater/Figure/Section) inside an existing Hyperlink's scope; detected via GetNextContextPosition scan from the range start.

Common situations: Applying link formatting to a selection that already contains a link; paste operations merging content inside a hyperlink; code that splits a Span at a Hyperlink boundary producing nested links.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextSchema.cs:897

        // It this context, the element or one of its ancestors is a Hyperlink.
        private static bool HasIllegalHyperlinkDescendant(TextElement element, bool throwIfIllegalDescendent)
        {
            TextPointer start = element.ElementStart;
            TextPointer end = element.ElementEnd;

            while (start.CompareTo(end) < 0)
            {
                TextPointerContext forwardContext = start.GetPointerContext(LogicalDirection.Forward);
                if (forwardContext == TextPointerContext.ElementStart)
                {
                    TextElement nextElement = (TextElement)start.GetAdjacentElement(LogicalDirection.Forward);

                    if (nextElement is Hyperlink ||
                        nextElement is AnchoredBlock)
                    {
                        if (throwIfIllegalDescendent)
                        {
                            throw new InvalidOperationException(SR.Format(SR.TextSchema_IllegalHyperlinkChild, nextElement.GetType()));
                        }
                        return true;
                    }
                }

                start = start.GetNextContextPosition(LogicalDirection.Forward);
            }
            return false;
        }

        private static bool AreBrushesEqual(Brush brush1, Brush brush2)
        {
            SolidColorBrush solidBrush1 = brush1 as SolidColorBrush;
            if (solidBrush1 != null)
            {
                return solidBrush1.Color.Equals(((SolidColorBrush)brush2).Color);
            }
            else

View on GitHub (pinned to 81131a70a4)