dotnet/wpf · error · InvalidOperationException
SR.TextPositionIsFrozen
Error message
SR.TextPositionIsFrozen
What it means
TextPointer.VerifyNotFrozen is called before mutating operations on a TextPointer. TextPointers can be frozen (e.g. those returned as static positions like Document.ContentStart in some contexts, or via FrozenTextPointer implementations) and frozen pointers are immutable. Any mutation attempt on a frozen pointer throws InvalidOperationException(SR.TextPositionIsFrozen).
Solutions
- Clone the pointer into a mutable one before mutating (e.g. richTextBox.CaretPosition or use the pointer's CreatePointer/GetInsertionPosition semantics).
- Check TextPointer.IsFrozen before mutating and obtain an unfrozen copy if needed.
- Only use live pointers from Selection/CaretPosition for edits; treat frozen/snapshot pointers as read-only.
Example fix
// before frozenPointer.MoveToPosition(target, LogicalDirection.Forward); // after var mutable = richTextBox.CaretPosition; mutable.MoveToPosition(target, LogicalDirection.Forward);
Defensive patterns
Strategy: type-guard
Validate before calling
if (pointer.IsFrozen) pointer = richTextBox.CaretPosition; // obtain a mutable pointer
Type guard
static bool IsMutable(TextPointer p) => !p.IsFrozen;
Try / catch
try { pointer.MoveToPosition(target, dir); }
catch (InvalidOperationException) { /* pointer frozen: obtain a live pointer and retry */ } Prevention
- Check IsFrozen before any mutation
- Treat snapshot/frozen pointers as read-only
- Use Selection/CaretPosition pointers for edits
When it happens
Trigger: Calling mutation methods (InsertTextInRun, MoveToPosition, MoveToNextContextPosition, InsertUIElement, etc.) on a frozen TextPointer instance — commonly the caret/selection pointers returned from frozen positions or pointer instances marked frozen by the editor.
Common situations: Storing a frozen pointer from an editor snapshot and later trying to move it; freezing via TextEditor snapshot APIs then mutating; confusing frozen positions with live selection positions (Selection.Start/CaretPosition).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- ArgumentNullException(paramName)
- SR.BadDistance
- SR.BadDistance
- SR.BadTextPositionOrder
- SR.Format(SR.BadTextPositionOrder, "start", "end")
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/11fa68c8620f9b69.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextPointer.cs:3951
SetNodeAndEdge(node.IncrementReferenceCount(edge), edge);
_generation = generation;
this.CaretUnitBoundaryCache = caretUnitBoundaryCache;
this.IsCaretUnitBoundaryCacheValid = isCaretUnitBoundaryCacheValid;
_layoutGeneration = layoutGeneration;
VerifyFlags();
tree.AssertTree();
AssertState();
}
// Throws an exception if this TextPointer is frozen.
private void VerifyNotFrozen()
{
if (this.IsFrozen)
{
throw new InvalidOperationException(SR.TextPositionIsFrozen);
}
}
// Inc/decs the position ref counts on TextTreeTextNodes as the navigator
// is repositioned.
// If the new ref is to a TextTreeTextNode, the node may be split.
// Returns the actual node referenced, which will always be newNode,
// unless newNode is a TextTreeTextNode that gets split. The caller
// should use the returned node to position navigators.
private TextTreeNode AdjustRefCounts(TextTreeNode newNode, ElementEdge newNodeEdge, TextTreeNode oldNode, ElementEdge oldNodeEdge)
{
TextTreeNode node;
// This test should walk the tree upwards to catch all errors...probably not worth the slowdown though.
Invariant.Assert(oldNode.ParentNode == null || oldNode.IsChildOfNode(oldNode.ParentNode), "Trying to add ref a dead node!");
Invariant.Assert(newNode.ParentNode == null || newNode.IsChildOfNode(newNode.ParentNode), "Trying to add ref a dead node!");
node = newNode;View on GitHub (pinned to 81131a70a4)