dotnet/wpf · error · InvalidOperationException
SR.FlowDocumentFormattingReentrancy
Error message
SR.FlowDocumentFormattingReentrancy
What it means
FlowDocumentFormatter.Format throws InvalidOperationException(SR.FlowDocumentFormattingReentrancy) when formatting is requested while another formatting pass is already in progress on the same FlowDocument (StructuralCache.IsFormattingInProgress is true). WPF's FlowDocument formatting state is not reentrant; nested calls would corrupt incremental layout state. The library fails fast instead of queueing the work.
Solutions
- Ensure the code path that re-triggers formatting is not invoked during an active formatting pass; defer it (Dispatcher.BeginInvoke) instead of calling synchronously.
- Never call Format/GetPage/GetPageAsync from within pagination callbacks; capture the request and process it after the current operation completes.
- If custom code mutates the document, batch changes and let layout complete before requesting new pages.
- Use the same DocumentPaginator instance rather than starting a second formatting pipeline concurrently.
Example fix
// before
void OnPaginationCompleted(object sender, EventArgs e) {
paginator.GetPageAsync(0, null); // may still be inside formatting
}
// after
void OnPaginationCompleted(object sender, EventArgs e) {
Dispatcher.BeginInvoke(new Action(() => paginator.GetPageAsync(0, null)),
System.Windows.Threading.DispatcherPriority.Background);
} Defensive patterns
Strategy: validation
Validate before calling
if (_document.StructuralCache.IsFormattingInProgress)
return; // or defer; do not call Format now Type guard
bool CanFormat(FlowDocument doc) => !doc.StructuralCache.IsFormattingInProgress && !doc.StructuralCache.IsContentChangeInProgress;
Try / catch
try { formatter.Format(offset); }
catch (InvalidOperationException ex) when (ex.Message.Contains("reentrancy") || ex.Message.Contains("formatting")) {
Dispatcher.BeginInvoke(new Action(() => formatter.Format(offset)), DispatcherPriority.Background);
} Prevention
- Never call formatting/pagination APIs from within pagination callbacks
- Defer re-layout requests with Dispatcher.BeginInvoke
- Keep one formatting pipeline per FlowDocument
- Batch document mutations outside layout passes
When it happens
Trigger: Calling FlowDocumentFormatter.Format (directly or via layout/pagination) from inside code that runs while formatting is in progress — e.g. inside a GetPage/GetPageAsync callback, a DocumentPaginator event handler, or a property-changed handler that synchronously re-triggers Format.
Common situations: Custom DocumentPaginator subclasses that call back into document APIs during OnGetPage; event handlers that modify layout-triggering properties while pagination is running; synchronously forcing layout from within a Print/print-chain callback.
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
- SR.FlowDocumentFormattingReentrancy
- SR.FlowDocumentInvalidContnetChange
- SR.TextContainerChangingReentrancyInvalid
- SR.TextContainerChangingReentrancyInvalid
- args
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/446d31866c437c87.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/FlowDocumentFormatter.cs:59
// Internal Methods
//
//-------------------------------------------------------------------
#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 onceView on GitHub (pinned to 81131a70a4)