dotnet/wpf · error · InvalidOperationException

SR.DocumentReadOnly

Error message

SR.DocumentReadOnly

What it means

DocumentSequenceTextPointer wraps a read-only document sequence, so the ITextPointer.InsertTextInRun implementation unconditionally throws InvalidOperationException with SR.DocumentReadOnly. FixedDocumentSequence content cannot be edited in place.

Solutions

  1. Do not edit DocumentSequence content via text pointers; the document is immutable
  2. Edit the underlying source documents (e.g., individual FlowDocuments) before generating the sequence
  3. Catch InvalidOperationException and treat the sequence as read-only in the UI (disable editing affordances)

Example fix

// before
ptr.InsertTextInRun("text"); // throws on FixedDocumentSequence
// after
if (ptr is DocumentSequenceTextPointer) { /* sequence is read-only; edit source document instead */ }
else { ptr.InsertTextInRun("text"); }
Defensive patterns

Strategy: type-guard

Validate before calling

if (pointer is DocumentSequenceTextPointer)
    throw new InvalidOperationException("Document sequence is read-only; InsertTextInRun is not allowed");

Type guard

bool IsEditablePointer(ITextPointer p) => p is not DocumentSequenceTextPointer;

Try / catch

try { ptr.InsertTextInRun(text); }
catch (InvalidOperationException ex) when (ex.Message.Contains("read-only")) { /* disable editing UI */ }

Prevention

When it happens

Trigger: Calling ITextPointer.InsertTextInRun on a pointer obtained from a FixedDocumentSequence/DocumentSequenceTextContainer, typically from text editing or IME composition code.

Common situations: Attempting programmatic text insertion into an XPS/FixedDocumentSequence view; TextEditor pipelines treating the sequence container as editable like a FlowDocument.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/DocumentSequenceTextPointer.cs:226

        }

        /// <summary>
        /// <see cref="ITextPointer.GetFrozenPointer"/>
        /// </summary>
        ITextPointer ITextPointer.GetFrozenPointer(LogicalDirection logicalDirection)
        {
            return TextPointerBase.GetFrozenPointer(this, logicalDirection);
        }

        /// <summary>
        /// Inserts text at a specified position.
        /// </summary>
        /// <param name="textData">
        /// Text to insert.
        /// </param>
        void ITextPointer.InsertTextInRun(string textData)
        {
            throw new InvalidOperationException(SR.DocumentReadOnly);
        }

        /// <summary>
        /// Removes content covered by a pair of positions.
        /// </summary>
        /// <param name="limit">
        /// Position following the last symbol to delete.  endPosition must be
        /// scoped by the same text element as startPosition.
        /// </param>
        void ITextPointer.DeleteContentToPosition(ITextPointer limit)
        {
            throw new InvalidOperationException(SR.DocumentReadOnly);
        }

        // Candidate for replacing MoveToNextContextPosition for immutable TextPointer model
        ITextPointer ITextPointer.GetNextContextPosition(LogicalDirection direction)
        {
            ITextPointer pointer = ((ITextPointer)this).CreatePointer();

View on GitHub (pinned to 81131a70a4)