dotnet/wpf · error · InvalidOperationException

SR.RichTextBox_CantSetDocumentInsideChangeBlock

Error message

SR.RichTextBox_CantSetDocumentInsideChangeBlock

What it means

While an editing change block is open (e.g. inside TextEditor.ChangeBlock, used by undo units and programmatic edits), RichTextBox.Document cannot be replaced. The setter throws InvalidOperationException(SR.RichTextBox_CantSetDocumentInsideChangeBlock) because swapping the text container mid-change would corrupt the undo stack and active selection.

Solutions

  1. Defer the assignment out of the change block, e.g. Dispatcher.BeginInvoke to run after editing completes.
  2. Close the change block (EndChange/Dispose) before assigning Document.
  3. Restructure so Document replacement is not triggered by editing events of the same control.

Example fix

// before
void OnTextChanged(...) { richTextBox.Document = newDoc; }
// after
void OnTextChanged(...) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); }
Defensive patterns

Strategy: try-catch

Validate before calling

if (richTextBox.TextSelectionInternal?.ChangeBlockLevel > 0)
    Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc);
else
    richTextBox.Document = newDoc;

Type guard

bool OutsideChangeBlock(RichTextBox rtb) => rtb.TextSelectionInternal == null || rtb.TextSelectionInternal.ChangeBlockLevel == 0;

Try / catch

try { richTextBox.Document = newDoc; }
catch (InvalidOperationException) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); }

Prevention

When it happens

Trigger: Setting Document from within an open change block: inside BeginChange/EndChange scopes, editing-event handlers such as TextChanged or selection events fired during a change block, or re-entrancy from a TextChanged handler that resets the Document.

Common situations: Reacting to TextChanged by replacing the Document; undo/redo callbacks that swap documents; custom commands wrapping edits in BeginChange and then assigning Document.

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


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/RichTextBox.cs:365

            {
                Invariant.Assert(_document != null);
                return _document;
            }

            set
            {
                ArgumentNullException.ThrowIfNull(value);

                if (value != _document &&
                    value.StructuralCache != null && value.StructuralCache.TextContainer != null && 
                    value.StructuralCache.TextContainer.TextSelection != null)
                {
                    throw new ArgumentException(SR.RichTextBox_DocumentBelongsToAnotherRichTextBoxAlready);
                }

                if (_document != null && this.TextSelectionInternal.ChangeBlockLevel > 0)
                {
                    throw new InvalidOperationException(SR.RichTextBox_CantSetDocumentInsideChangeBlock);
                }

                if (value == _document)
                {
                    // Same document nothing to do.
                    return;
                }

                // Identify the case for the _document initialization
                bool initialSetting = _document == null;
                
                // Detach existing FlowDocument
                if (_document != null)
                {
                    // Detach PageSize change listener
                    _document.PageSizeChanged -= new EventHandler(this.OnPageSizeChangedHandler);

                    // Remove the document from the logical tree

View on GitHub (pinned to 81131a70a4)