dotnet/wpf · error · InvalidOperationException

SR.TextContainerChangingReentrancyInvalid

Error message

SR.TextContainerChangingReentrancyInvalid

What it means

FlowDocumentFormatter.Format throws InvalidOperationException(SR.TextContainerChangingReentrancyInvalid) when formatting is requested while a content change (edit) is in progress on the document's TextContainer (StructuralCache.IsContentChangeInProgress). Formatting cannot run against a TextContainer that is mid-change; WPF throws to protect container/incremental-format state.

Solutions

  1. Move formatting/pagination requests out of the change-notification scope; defer with Dispatcher.BeginInvoke until the change completes.
  2. Do not call layout/pagination APIs inside TextChanged/ContentChanged handlers on the same FlowDocument.
  3. Batch document edits so change scopes are short and formatting is requested afterward.
  4. Wrap mutations so that any re-layout is triggered once after all edits finish.

Example fix

// before
document.StructuralCache.TextContainer.Changed += (s, e) => {
    formatter.Format(0); // throws: change in progress
};
// after
document.StructuralCache.TextContainer.Changed += (s, e) => {
    Dispatcher.BeginInvoke(new Action(() => formatter.Format(0)),
        System.Windows.Threading.DispatcherPriority.Background);
};
Defensive patterns

Strategy: validation

Validate before calling

if (_document.StructuralCache.IsContentChangeInProgress)
    return; // defer formatting until the edit completes

Type guard

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

Try / catch

try { formatter.Format(offset); }
catch (InvalidOperationException ex) {
    Dispatcher.BeginInvoke(new Action(() => formatter.Format(offset)), DispatcherPriority.Background);
}

Prevention

When it happens

Trigger: Calling FlowDocumentFormatter.Format (directly or through layout/pagination APIs) from inside a TextContainer change scope — e.g. inside a TextContainer.Change event handler, inside TextPointer insert/remove operations, or from code executed during a document mutation.

Common situations: Text-changed event handlers that force layout or paginate synchronously; document edits performed inside layout/pagination notifications; RichTextBox/TextBox event handlers that trigger printing or pagination of the same FlowDocument.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentFormatter.cs:63

        #region Internal Methods

        /// <summary>
        /// Formatts content.
        /// </summary>
        /// <param name="constraint">Constraint size.</param>
        internal void Format(Size constraint)
        {
            Thickness pageMargin;
            Size pageSize;

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

            // Check if we can continue with formatting without nuking incremental udpate info.
            if (_document.StructuralCache.IsFormattedOnce)
            {
                if (!_lastFormatSuccessful)
                {
                    // We cannot resolve update info if last formatting was unsuccessful.
                    _document.StructuralCache.InvalidateFormatCache(true);
                }
                if (!_arrangedAfterFormat && (!_document.StructuralCache.ForceReformat || !_document.StructuralCache.DestroyStructure))
                {
                    // Need to clear update info by running arrange process.
                    // This is necessary, because Format may be called more than once
                    // before Arrange is called. But PTS is not able to merge update info.
                    // To protect against loosing incremental changes delta, need
                    // to arrange the page and create all necessary visuals.
                    _documentPage.Arrange(_documentPage.ContentSize);

View on GitHub (pinned to 81131a70a4)