dotnet/wpf · error · InvalidOperationException

SR.TextContainerChangingReentrancyInvalid

Error message

SR.TextContainerChangingReentrancyInvalid

What it means

FlowDocumentPaginator.GetPageAsync throws InvalidOperationException(SR.TextContainerChangingReentrancyInvalid) when called while a content change is in progress on the document's TextContainer (StructuralCache.IsContentChangeInProgress). Formatting cannot start against a mutating TextContainer; WPF throws rather than produce inconsistent pages.

Solutions

  1. Defer GetPageAsync until the content change completes — post it with Dispatcher.BeginInvoke from change handlers.
  2. Never request pagination from within ContentChanged/TextChanged handlers on the same document.
  3. Complete all document edits before initiating printing/pagination.
  4. If you must react to edits, mark a dirty flag and paginate on a subsequent dispatcher pass.

Example fix

// before
richTextBox.TextChanged += (s, e) => {
    paginator.GetPageAsync(0, null); // content change in progress
};
// after
richTextBox.TextChanged += (s, e) => {
    Dispatcher.BeginInvoke(new Action(() => paginator.GetPageAsync(0, null)),
        System.Windows.Threading.DispatcherPriority.Background);
};
Defensive patterns

Strategy: validation

Validate before calling

if (!document.StructuralCache.IsContentChangeInProgress &&
    !document.StructuralCache.IsFormattingInProgress)
    paginator.GetPageAsync(pageNumber, userState);

Type guard

bool CanPaginate(FlowDocument doc) => !doc.StructuralCache.IsContentChangeInProgress && !doc.StructuralCache.IsFormattingInProgress;

Try / catch

try { paginator.GetPageAsync(n, state); }
catch (InvalidOperationException) {
    Dispatcher.BeginInvoke(new Action(() => paginator.GetPageAsync(n, state)), DispatcherPriority.Background);
}

Prevention

When it happens

Trigger: Calling GetPageAsync while the FlowDocument is inside a TextContainer change scope — e.g. from a TextChanged/ContentChanged event handler, from code executed during paragraph/text insertion, or from a nested API call that mutates the document while pagination is requested on the same call stack.

Common situations: Handlers that update document text and immediately request pages; RichTextBox automation that edits content and paginates in the same event; printing triggered from within a text-editing transaction.

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/d1e58408fecd6335. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentPaginator.cs:83

        /// <exception cref="ArgumentOutOfRangeException">
        /// Throws ArgumentOutOfRangeException if PageNumber is negative.
        /// </exception>
        public override void GetPageAsync(int pageNumber, object userState)
        {
            // Page number cannot be negative.
            if (pageNumber < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(pageNumber), SR.IDPNegativePageNumber);
            }

            // Reentrancy check.
            if (_document.StructuralCache.IsFormattingInProgress)
            {
                throw new InvalidOperationException(SR.FlowDocumentFormattingReentrancy);
            }
            if (_document.StructuralCache.IsContentChangeInProgress)
            {
                throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid);
            }

            DocumentPage page = null;

            if (!_backgroundPagination)
            {
                page = GetPage(pageNumber);
            }
            else
            {
                // If entire content has been already pre-paginated (BreakRecordTable is clean)
                // and requesting non-existing page number, return DocumentPage.Missing.
                if (_brt.IsClean && !_brt.HasPageBreakRecord(pageNumber))
                {
                    page = DocumentPage.Missing;
                }

                if (_brt.HasPageBreakRecord(pageNumber))

View on GitHub (pinned to 81131a70a4)