dotnet/wpf · error · InvalidOperationException
SR.TextRangeEdit_InvalidStructuralPropertyApply (property…
Error message
SR.TextRangeEdit_InvalidStructuralPropertyApply (property, nonMergeableAncestor)
What it means
TextRangeEdit throws InvalidOperationException when applying a structural (mergeable) formatting property would require splitting through a non-mergeable ancestor element. During ApplyPropertyToTextVirtual the edit engine walks up to find a common ancestor; if property != null and the parent chain diverges through an element that cannot be merged (nonMergeableAncestor), the structural apply is invalid. This is an internal invariant violation surfaced to public ApplyPropertyValue paths.
Solutions
- Apply the property per logical block (iterate Blocks and set on each Paragraph) instead of over the whole range
- Constrain the selection so it does not cross non-mergeable structural boundaries (table cells, sections)
- Use direct property assignment on the TextElement rather than range ApplyPropertyValue for structural properties
Example fix
// before
selection.ApplyPropertyValue(Block.TextAlignmentProperty, TextAlignment.Center);
// after
foreach (Paragraph p in GetSelectedParagraphs(selection))
p.TextAlignment = TextAlignment.Center; Defensive patterns
Strategy: try-catch
Validate before calling
bool crossesCells = /* determine via selection start/end ancestor checks */ false; if (crossesCells) ApplyPerBlockInstead();
Try / catch
try { selection.ApplyPropertyValue(prop, value); } catch (InvalidOperationException ex) { /* structural apply across non-mergeable ancestors — apply per block */ } Prevention
- Avoid applying structural/paragraph properties to selections spanning tables, lists, or floats
- Iterate logical blocks and set properties element-by-element
- Test formatting code against heterogeneous document content
When it happens
Trigger: Applying a paragraph-level structural property (e.g. FlowDirection, TextAlignment) to a selection spanning multiple block elements whose ancestors cannot be merged — e.g. selection crossing TableCell boundaries or nested lists with differing structure.
Common situations: Selections spanning table cells, list items, or floated figures; programmatic formatting of whole-document content with mixed structure; applying paragraph properties via TextSelection.ApplyPropertyValue over heterogeneous content.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- OutOfMemoryException
- SR.Format(SR.UnexpectedValueTypeForDataTrigger…
- SR.TextSchema_IllegalHyperlinkChild (nextElement.GetType())
- SR.UndoUnitCantBeOpenedTwice
- SR.UnexpectedValueTypeForCondition
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/02d33215402bfe88.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextRangeEdit.cs:2286
nonMergeableAncestor = parent;
commonAncestor = parent;
break;
}
}
// Try to reach the start non-mergeable or original commonAncestor from end.
for (parent = (Inline)end.Parent; parent != commonAncestor; parent = (Inline)parent.Parent)
{
if (!TextSchema.IsMergeableInline(parent.GetType()))
{
nonMergeableAncestor = parent;
break;
}
}
if (property != null && parent != commonAncestor)
{
throw new InvalidOperationException(SR.Format(SR.TextRangeEdit_InvalidStructuralPropertyApply, property, nonMergeableAncestor));
}
return (parent == commonAncestor);
}
#endregion Private Methods
#region Private Types
/// <summary>
/// This class imposes value ranges, considered valid by editing code, for Dependency properties of type double.
/// In other words this class defines value range policies for DPs of type double, in editing context.
/// </summary>
internal static class DoublePropertyBounds
{
/// <summary>
/// Validates the value and if it's in permitable range then the <paramref name="value"/> is returned.
/// Oterwise closest bound(lower/upper) of the range is returned.
/// </summary>View on GitHub (pinned to 81131a70a4)